1 /* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.annotations.Beta; 22 import com.google.common.annotations.GwtCompatible; 23 import com.google.common.base.Optional; 24 25 import java.util.ArrayDeque; 26 import java.util.BitSet; 27 import java.util.Deque; 28 import java.util.Iterator; 29 30 /** 31 * A variant of {@link TreeTraverser} for binary trees, providing additional traversals specific to 32 * binary trees. 33 * 34 * @author Louis Wasserman 35 * @since 15.0 36 */ 37 @Beta 38 @GwtCompatible(emulated = true) 39 public abstract class BinaryTreeTraverser<T> extends TreeTraverser<T> { 40 // TODO(user): make this GWT-compatible when we've checked in ArrayDeque and BitSet emulation 41 42 /** 43 * Returns the left child of the specified node, or {@link Optional#absent()} if the specified 44 * node has no left child. 45 */ leftChild(T root)46 public abstract Optional<T> leftChild(T root); 47 48 /** 49 * Returns the right child of the specified node, or {@link Optional#absent()} if the specified 50 * node has no right child. 51 */ rightChild(T root)52 public abstract Optional<T> rightChild(T root); 53 54 /** 55 * Returns the children of this node, in left-to-right order. 56 */ 57 @Override children(final T root)58 public final Iterable<T> children(final T root) { 59 checkNotNull(root); 60 return new FluentIterable<T>() { 61 @Override 62 public Iterator<T> iterator() { 63 return new AbstractIterator<T>() { 64 boolean doneLeft; 65 boolean doneRight; 66 67 @Override 68 protected T computeNext() { 69 if (!doneLeft) { 70 doneLeft = true; 71 Optional<T> left = leftChild(root); 72 if (left.isPresent()) { 73 return left.get(); 74 } 75 } 76 if (!doneRight) { 77 doneRight = true; 78 Optional<T> right = rightChild(root); 79 if (right.isPresent()) { 80 return right.get(); 81 } 82 } 83 return endOfData(); 84 } 85 }; 86 } 87 }; 88 } 89 90 @Override 91 UnmodifiableIterator<T> preOrderIterator(T root) { 92 return new PreOrderIterator(root); 93 } 94 95 /* 96 * Optimized implementation of preOrderIterator for binary trees. 97 */ 98 private final class PreOrderIterator extends UnmodifiableIterator<T> 99 implements PeekingIterator<T> { 100 private final Deque<T> stack; 101 102 PreOrderIterator(T root) { 103 this.stack = new ArrayDeque<T>(); 104 stack.addLast(root); 105 } 106 107 @Override 108 public boolean hasNext() { 109 return !stack.isEmpty(); 110 } 111 112 @Override 113 public T next() { 114 T result = stack.removeLast(); 115 pushIfPresent(stack, rightChild(result)); 116 pushIfPresent(stack, leftChild(result)); 117 return result; 118 } 119 120 @Override 121 public T peek() { 122 return stack.getLast(); 123 } 124 } 125 126 @Override 127 UnmodifiableIterator<T> postOrderIterator(T root) { 128 return new PostOrderIterator(root); 129 } 130 131 /* 132 * Optimized implementation of postOrderIterator for binary trees. 133 */ 134 private final class PostOrderIterator extends UnmodifiableIterator<T> { 135 private final Deque<T> stack; 136 private final BitSet hasExpanded; 137 138 PostOrderIterator(T root) { 139 this.stack = new ArrayDeque<T>(); 140 stack.addLast(root); 141 this.hasExpanded = new BitSet(); 142 } 143 144 @Override 145 public boolean hasNext() { 146 return !stack.isEmpty(); 147 } 148 149 @Override 150 public T next() { 151 while (true) { 152 T node = stack.getLast(); 153 boolean expandedNode = hasExpanded.get(stack.size() - 1); 154 if (expandedNode) { 155 stack.removeLast(); 156 hasExpanded.clear(stack.size()); 157 return node; 158 } else { 159 hasExpanded.set(stack.size() - 1); 160 pushIfPresent(stack, rightChild(node)); 161 pushIfPresent(stack, leftChild(node)); 162 } 163 } 164 } 165 } 166 167 // TODO(user): see if any significant optimizations are possible for breadthFirstIterator 168 169 public final FluentIterable<T> inOrderTraversal(final T root) { 170 checkNotNull(root); 171 return new FluentIterable<T>() { 172 @Override 173 public UnmodifiableIterator<T> iterator() { 174 return new InOrderIterator(root); 175 } 176 }; 177 } 178 179 private final class InOrderIterator extends AbstractIterator<T> { 180 private final Deque<T> stack; 181 private final BitSet hasExpandedLeft; 182 183 InOrderIterator(T root) { 184 this.stack = new ArrayDeque<T>(); 185 this.hasExpandedLeft = new BitSet(); 186 stack.addLast(root); 187 } 188 189 @Override 190 protected T computeNext() { 191 while (!stack.isEmpty()) { 192 T node = stack.getLast(); 193 if (hasExpandedLeft.get(stack.size() - 1)) { 194 stack.removeLast(); 195 hasExpandedLeft.clear(stack.size()); 196 pushIfPresent(stack, rightChild(node)); 197 return node; 198 } else { 199 hasExpandedLeft.set(stack.size() - 1); 200 pushIfPresent(stack, leftChild(node)); 201 } 202 } 203 return endOfData(); 204 } 205 } 206 207 private static <T> void pushIfPresent(Deque<T> stack, Optional<T> node) { 208 if (node.isPresent()) { 209 stack.addLast(node.get()); 210 } 211 } 212 } 213