214 lines
5.2 KiB
Java
214 lines
5.2 KiB
Java
/*
|
|
* Copyright (C) 2012 The Guava Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.common.collect;
|
|
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
import java.util.ArrayDeque;
|
|
import java.util.BitSet;
|
|
import java.util.Deque;
|
|
import java.util.Iterator;
|
|
|
|
import com.google.common.annotations.Beta;
|
|
import com.google.common.annotations.GwtCompatible;
|
|
import com.google.common.base.Optional;
|
|
|
|
/**
|
|
* A variant of {@link TreeTraverser} for binary trees, providing additional
|
|
* traversals specific to binary trees.
|
|
*
|
|
* @author Louis Wasserman
|
|
* @since 15.0
|
|
*/
|
|
@Beta
|
|
@GwtCompatible(emulated = true)
|
|
public abstract class BinaryTreeTraverser<T> extends TreeTraverser<T> {
|
|
// TODO(user): make this GWT-compatible when we've checked in ArrayDeque and
|
|
// BitSet emulation
|
|
|
|
/**
|
|
* Returns the left child of the specified node, or {@link Optional#absent()} if
|
|
* the specified node has no left child.
|
|
*/
|
|
public abstract Optional<T> leftChild(T root);
|
|
|
|
/**
|
|
* Returns the right child of the specified node, or {@link Optional#absent()}
|
|
* if the specified node has no right child.
|
|
*/
|
|
public abstract Optional<T> rightChild(T root);
|
|
|
|
/**
|
|
* Returns the children of this node, in left-to-right order.
|
|
*/
|
|
@Override
|
|
public final Iterable<T> children(final T root) {
|
|
checkNotNull(root);
|
|
return new FluentIterable<T>() {
|
|
@Override
|
|
public Iterator<T> iterator() {
|
|
return new AbstractIterator<T>() {
|
|
boolean doneLeft;
|
|
boolean doneRight;
|
|
|
|
@Override
|
|
protected T computeNext() {
|
|
if (!doneLeft) {
|
|
doneLeft = true;
|
|
Optional<T> left = leftChild(root);
|
|
if (left.isPresent()) {
|
|
return left.get();
|
|
}
|
|
}
|
|
if (!doneRight) {
|
|
doneRight = true;
|
|
Optional<T> right = rightChild(root);
|
|
if (right.isPresent()) {
|
|
return right.get();
|
|
}
|
|
}
|
|
return endOfData();
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
UnmodifiableIterator<T> preOrderIterator(T root) {
|
|
return new PreOrderIterator(root);
|
|
}
|
|
|
|
/*
|
|
* Optimized implementation of preOrderIterator for binary trees.
|
|
*/
|
|
private final class PreOrderIterator extends UnmodifiableIterator<T> implements PeekingIterator<T> {
|
|
private final Deque<T> stack;
|
|
|
|
PreOrderIterator(T root) {
|
|
this.stack = new ArrayDeque<T>();
|
|
stack.addLast(root);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
return !stack.isEmpty();
|
|
}
|
|
|
|
@Override
|
|
public T next() {
|
|
T result = stack.removeLast();
|
|
pushIfPresent(stack, rightChild(result));
|
|
pushIfPresent(stack, leftChild(result));
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public T peek() {
|
|
return stack.getLast();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
UnmodifiableIterator<T> postOrderIterator(T root) {
|
|
return new PostOrderIterator(root);
|
|
}
|
|
|
|
/*
|
|
* Optimized implementation of postOrderIterator for binary trees.
|
|
*/
|
|
private final class PostOrderIterator extends UnmodifiableIterator<T> {
|
|
private final Deque<T> stack;
|
|
private final BitSet hasExpanded;
|
|
|
|
PostOrderIterator(T root) {
|
|
this.stack = new ArrayDeque<T>();
|
|
stack.addLast(root);
|
|
this.hasExpanded = new BitSet();
|
|
}
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
return !stack.isEmpty();
|
|
}
|
|
|
|
@Override
|
|
public T next() {
|
|
while (true) {
|
|
T node = stack.getLast();
|
|
boolean expandedNode = hasExpanded.get(stack.size() - 1);
|
|
if (expandedNode) {
|
|
stack.removeLast();
|
|
hasExpanded.clear(stack.size());
|
|
return node;
|
|
} else {
|
|
hasExpanded.set(stack.size() - 1);
|
|
pushIfPresent(stack, rightChild(node));
|
|
pushIfPresent(stack, leftChild(node));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(user): see if any significant optimizations are possible for
|
|
// breadthFirstIterator
|
|
|
|
public final FluentIterable<T> inOrderTraversal(final T root) {
|
|
checkNotNull(root);
|
|
return new FluentIterable<T>() {
|
|
@Override
|
|
public UnmodifiableIterator<T> iterator() {
|
|
return new InOrderIterator(root);
|
|
}
|
|
};
|
|
}
|
|
|
|
private final class InOrderIterator extends AbstractIterator<T> {
|
|
private final Deque<T> stack;
|
|
private final BitSet hasExpandedLeft;
|
|
|
|
InOrderIterator(T root) {
|
|
this.stack = new ArrayDeque<T>();
|
|
this.hasExpandedLeft = new BitSet();
|
|
stack.addLast(root);
|
|
}
|
|
|
|
@Override
|
|
protected T computeNext() {
|
|
while (!stack.isEmpty()) {
|
|
T node = stack.getLast();
|
|
if (hasExpandedLeft.get(stack.size() - 1)) {
|
|
stack.removeLast();
|
|
hasExpandedLeft.clear(stack.size());
|
|
pushIfPresent(stack, rightChild(node));
|
|
return node;
|
|
} else {
|
|
hasExpandedLeft.set(stack.size() - 1);
|
|
pushIfPresent(stack, leftChild(node));
|
|
}
|
|
}
|
|
return endOfData();
|
|
}
|
|
}
|
|
|
|
private static <T> void pushIfPresent(Deque<T> stack, Optional<T> node) {
|
|
if (node.isPresent()) {
|
|
stack.addLast(node.get());
|
|
}
|
|
}
|
|
}
|