001/* 002 * Copyright (C) 2012 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.base.Optional; 024import java.util.ArrayDeque; 025import java.util.BitSet; 026import java.util.Deque; 027import java.util.Iterator; 028import java.util.function.Consumer; 029 030/** 031 * A variant of {@link TreeTraverser} for binary trees, providing additional traversals specific to 032 * binary trees. 033 * 034 * @author Louis Wasserman 035 * @since 15.0 036 */ 037@Beta 038@GwtCompatible 039public abstract class BinaryTreeTraverser<T> extends TreeTraverser<T> { 040 041 /** 042 * Returns the left child of the specified node, or {@link Optional#absent()} if the specified 043 * node has no left child. 044 */ 045 public abstract Optional<T> leftChild(T root); 046 047 /** 048 * Returns the right child of the specified node, or {@link Optional#absent()} if the specified 049 * node has no right child. 050 */ 051 public abstract Optional<T> rightChild(T root); 052 053 /** 054 * Returns the children of this node, in left-to-right order. 055 */ 056 @Override 057 public final Iterable<T> children(final T root) { 058 checkNotNull(root); 059 return new FluentIterable<T>() { 060 @Override 061 public Iterator<T> iterator() { 062 return new AbstractIterator<T>() { 063 boolean doneLeft; 064 boolean doneRight; 065 066 @Override 067 protected T computeNext() { 068 if (!doneLeft) { 069 doneLeft = true; 070 Optional<T> left = leftChild(root); 071 if (left.isPresent()) { 072 return left.get(); 073 } 074 } 075 if (!doneRight) { 076 doneRight = true; 077 Optional<T> right = rightChild(root); 078 if (right.isPresent()) { 079 return right.get(); 080 } 081 } 082 return endOfData(); 083 } 084 }; 085 } 086 087 @Override 088 public void forEach(Consumer<? super T> action) { 089 acceptIfPresent(action, leftChild(root)); 090 acceptIfPresent(action, rightChild(root)); 091 } 092 }; 093 } 094 095 @Override 096 UnmodifiableIterator<T> preOrderIterator(T root) { 097 return new PreOrderIterator(root); 098 } 099 100 /* 101 * Optimized implementation of preOrderIterator for binary trees. 102 */ 103 private final class PreOrderIterator extends UnmodifiableIterator<T> 104 implements PeekingIterator<T> { 105 private final Deque<T> stack; 106 107 PreOrderIterator(T root) { 108 this.stack = new ArrayDeque<T>(8); 109 stack.addLast(root); 110 } 111 112 @Override 113 public boolean hasNext() { 114 return !stack.isEmpty(); 115 } 116 117 @Override 118 public T next() { 119 T result = stack.removeLast(); 120 pushIfPresent(stack, rightChild(result)); 121 pushIfPresent(stack, leftChild(result)); 122 return result; 123 } 124 125 @Override 126 public T peek() { 127 return stack.getLast(); 128 } 129 } 130 131 @Override 132 UnmodifiableIterator<T> postOrderIterator(T root) { 133 return new PostOrderIterator(root); 134 } 135 136 /* 137 * Optimized implementation of postOrderIterator for binary trees. 138 */ 139 private final class PostOrderIterator extends UnmodifiableIterator<T> { 140 private final Deque<T> stack; 141 private final BitSet hasExpanded; 142 143 PostOrderIterator(T root) { 144 this.stack = new ArrayDeque<T>(8); 145 stack.addLast(root); 146 this.hasExpanded = new BitSet(); 147 } 148 149 @Override 150 public boolean hasNext() { 151 return !stack.isEmpty(); 152 } 153 154 @Override 155 public T next() { 156 while (true) { 157 T node = stack.getLast(); 158 boolean expandedNode = hasExpanded.get(stack.size() - 1); 159 if (expandedNode) { 160 stack.removeLast(); 161 hasExpanded.clear(stack.size()); 162 return node; 163 } else { 164 hasExpanded.set(stack.size() - 1); 165 pushIfPresent(stack, rightChild(node)); 166 pushIfPresent(stack, leftChild(node)); 167 } 168 } 169 } 170 } 171 172 // TODO(lowasser): see if any significant optimizations are possible for breadthFirstIterator 173 174 public final FluentIterable<T> inOrderTraversal(final T root) { 175 checkNotNull(root); 176 return new FluentIterable<T>() { 177 @Override 178 public UnmodifiableIterator<T> iterator() { 179 return new InOrderIterator(root); 180 } 181 182 @Override 183 public void forEach(Consumer<? super T> action) { 184 checkNotNull(action); 185 new Consumer<T>() { 186 @Override 187 public void accept(T t) { 188 acceptIfPresent(this, leftChild(t)); 189 action.accept(t); 190 acceptIfPresent(this, rightChild(t)); 191 } 192 }.accept(root); 193 } 194 }; 195 } 196 197 private final class InOrderIterator extends AbstractIterator<T> { 198 private final Deque<T> stack; 199 private final BitSet hasExpandedLeft; 200 201 InOrderIterator(T root) { 202 this.stack = new ArrayDeque<T>(8); 203 this.hasExpandedLeft = new BitSet(); 204 stack.addLast(root); 205 } 206 207 @Override 208 protected T computeNext() { 209 while (!stack.isEmpty()) { 210 T node = stack.getLast(); 211 if (hasExpandedLeft.get(stack.size() - 1)) { 212 stack.removeLast(); 213 hasExpandedLeft.clear(stack.size()); 214 pushIfPresent(stack, rightChild(node)); 215 return node; 216 } else { 217 hasExpandedLeft.set(stack.size() - 1); 218 pushIfPresent(stack, leftChild(node)); 219 } 220 } 221 return endOfData(); 222 } 223 } 224 225 private static <T> void pushIfPresent(Deque<T> stack, Optional<T> node) { 226 if (node.isPresent()) { 227 stack.addLast(node.get()); 228 } 229 } 230 231 private static <T> void acceptIfPresent(Consumer<? super T> action, Optional<T> node) { 232 if (node.isPresent()) { 233 action.accept(node.get()); 234 } 235 } 236}