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.BitSet;
026import java.util.Iterator;
027import java.util.LinkedList;
028
029/**
030 * A variant of {@link TreeTraverser} for binary trees, providing additional traversals specific to
031 * binary trees.
032 *
033 * @author Louis Wasserman
034 * @since 15.0
035 */
036@Beta
037@GwtCompatible(emulated = true)
038public abstract class BinaryTreeTraverser<T> extends TreeTraverser<T> {
039  // TODO(user): make this GWT-compatible when we've checked in LinkedList and BitSet emulation
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  }
088
089  @Override
090  UnmodifiableIterator<T> preOrderIterator(T root) {
091    return new PreOrderIterator(root);
092  }
093
094  /*
095   * Optimized implementation of preOrderIterator for binary trees.
096   */
097  private final class PreOrderIterator extends UnmodifiableIterator<T>
098      implements PeekingIterator<T> {
099    private final LinkedList<T> stack;
100
101    PreOrderIterator(T root) {
102      this.stack = new LinkedList<T>();
103      stack.addLast(root);
104    }
105
106    @Override
107    public boolean hasNext() {
108      return !stack.isEmpty();
109    }
110
111    @Override
112    public T next() {
113      T result = stack.removeLast();
114      pushIfPresent(stack, rightChild(result));
115      pushIfPresent(stack, leftChild(result));
116      return result;
117    }
118
119    @Override
120    public T peek() {
121      return stack.getLast();
122    }
123  }
124
125  @Override
126  UnmodifiableIterator<T> postOrderIterator(T root) {
127    return new PostOrderIterator(root);
128  }
129
130  /*
131   * Optimized implementation of postOrderIterator for binary trees.
132   */
133  private final class PostOrderIterator extends UnmodifiableIterator<T> {
134    private final LinkedList<T> stack;
135    private final BitSet hasExpanded;
136
137    PostOrderIterator(T root) {
138      this.stack = new LinkedList<T>();
139      stack.addLast(root);
140      this.hasExpanded = new BitSet();
141    }
142
143    @Override
144    public boolean hasNext() {
145      return !stack.isEmpty();
146    }
147
148    @Override
149    public T next() {
150      while (true) {
151        T node = stack.getLast();
152        boolean expandedNode = hasExpanded.get(stack.size() - 1);
153        if (expandedNode) {
154          stack.removeLast();
155          hasExpanded.clear(stack.size());
156          return node;
157        } else {
158          hasExpanded.set(stack.size() - 1);
159          pushIfPresent(stack, rightChild(node));
160          pushIfPresent(stack, leftChild(node));
161        }
162      }
163    }
164  }
165
166  // TODO(user): see if any significant optimizations are possible for breadthFirstIterator
167
168  public final FluentIterable<T> inOrderTraversal(final T root) {
169    checkNotNull(root);
170    return new FluentIterable<T>() {
171      @Override
172      public UnmodifiableIterator<T> iterator() {
173        return new InOrderIterator(root);
174      }
175    };
176  }
177
178  private final class InOrderIterator extends AbstractIterator<T> {
179    private final LinkedList<T> stack;
180    private final BitSet hasExpandedLeft;
181
182    InOrderIterator(T root) {
183      this.stack = new LinkedList<T>();
184      this.hasExpandedLeft = new BitSet();
185      stack.addLast(root);
186    }
187
188    @Override
189    protected T computeNext() {
190      while (!stack.isEmpty()) {
191        T node = stack.getLast();
192        if (hasExpandedLeft.get(stack.size() - 1)) {
193          stack.removeLast();
194          hasExpandedLeft.clear(stack.size());
195          pushIfPresent(stack, rightChild(node));
196          return node;
197        } else {
198          hasExpandedLeft.set(stack.size() - 1);
199          pushIfPresent(stack, leftChild(node));
200        }
201      }
202      return endOfData();
203    }
204  }
205
206  private static <T> void pushIfPresent(LinkedList<T> stack, Optional<T> node) {
207    if (node.isPresent()) {
208      stack.addLast(node.get());
209    }
210  }
211}