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;
024
025import java.util.ArrayDeque;
026import java.util.BitSet;
027import java.util.Deque;
028import java.util.Iterator;
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(emulated = true)
039public abstract class BinaryTreeTraverser<T> extends TreeTraverser<T> {
040  // TODO(user): make this GWT-compatible when we've checked in ArrayDeque and BitSet emulation
041
042  /**
043   * Returns the left child of the specified node, or {@link Optional#absent()} if the specified
044   * node has no left child.
045   */
046  public abstract Optional<T> leftChild(T root);
047
048  /**
049   * Returns the right child of the specified node, or {@link Optional#absent()} if the specified
050   * node has no right child.
051   */
052  public abstract Optional<T> rightChild(T root);
053
054  /**
055   * Returns the children of this node, in left-to-right order.
056   */
057  @Override
058  public final Iterable<T> children(final T root) {
059    checkNotNull(root);
060    return new FluentIterable<T>() {
061      @Override
062      public Iterator<T> iterator() {
063        return new AbstractIterator<T>() {
064          boolean doneLeft;
065          boolean doneRight;
066
067          @Override
068          protected T computeNext() {
069            if (!doneLeft) {
070              doneLeft = true;
071              Optional<T> left = leftChild(root);
072              if (left.isPresent()) {
073                return left.get();
074              }
075            }
076            if (!doneRight) {
077              doneRight = true;
078              Optional<T> right = rightChild(root);
079              if (right.isPresent()) {
080                return right.get();
081              }
082            }
083            return endOfData();
084          }
085        };
086      }
087    };
088  }
089
090  @Override
091  UnmodifiableIterator<T> preOrderIterator(T root) {
092    return new PreOrderIterator(root);
093  }
094
095  /*
096   * Optimized implementation of preOrderIterator for binary trees.
097   */
098  private final class PreOrderIterator extends UnmodifiableIterator<T>
099      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}