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;
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 * @deprecated Use {@link com.google.common.graph.Traverser} instead. All instance methods except
036 *     for {@link #inOrderTraversal} have their equivalent on the result of {@code
037 *     Traverser.forTree(tree)} where {@code tree} implements {@code SuccessorsFunction}, which has
038 *     a similar API as {@link #children}.
039 *     <p>This class is scheduled to be removed in January 2018.
040 */
041@Deprecated
042@Beta
043@GwtCompatible
044public abstract class BinaryTreeTraverser<T> extends TreeTraverser<T> {
045
046  /**
047   * Returns the left child of the specified node, or {@link Optional#absent()} if the specified
048   * node has no left child.
049   */
050  public abstract Optional<T> leftChild(T root);
051
052  /**
053   * Returns the right child of the specified node, or {@link Optional#absent()} if the specified
054   * node has no right child.
055   */
056  public abstract Optional<T> rightChild(T root);
057
058  /**
059   * Returns the children of this node, in left-to-right order.
060   */
061  @Override
062  public final Iterable<T> children(final T root) {
063    checkNotNull(root);
064    return new FluentIterable<T>() {
065      @Override
066      public Iterator<T> iterator() {
067        return new AbstractIterator<T>() {
068          boolean doneLeft;
069          boolean doneRight;
070
071          @Override
072          protected T computeNext() {
073            if (!doneLeft) {
074              doneLeft = true;
075              Optional<T> left = leftChild(root);
076              if (left.isPresent()) {
077                return left.get();
078              }
079            }
080            if (!doneRight) {
081              doneRight = true;
082              Optional<T> right = rightChild(root);
083              if (right.isPresent()) {
084                return right.get();
085              }
086            }
087            return endOfData();
088          }
089        };
090      }
091    };
092  }
093
094  @Override
095  UnmodifiableIterator<T> preOrderIterator(T root) {
096    return new PreOrderIterator(root);
097  }
098
099  /*
100   * Optimized implementation of preOrderIterator for binary trees.
101   */
102  private final class PreOrderIterator extends UnmodifiableIterator<T>
103      implements PeekingIterator<T> {
104    private final Deque<T> stack;
105
106    PreOrderIterator(T root) {
107      this.stack = new ArrayDeque<T>(8);
108      stack.addLast(root);
109    }
110
111    @Override
112    public boolean hasNext() {
113      return !stack.isEmpty();
114    }
115
116    @Override
117    public T next() {
118      T result = stack.removeLast();
119      pushIfPresent(stack, rightChild(result));
120      pushIfPresent(stack, leftChild(result));
121      return result;
122    }
123
124    @Override
125    public T peek() {
126      return stack.getLast();
127    }
128  }
129
130  @Override
131  UnmodifiableIterator<T> postOrderIterator(T root) {
132    return new PostOrderIterator(root);
133  }
134
135  /*
136   * Optimized implementation of postOrderIterator for binary trees.
137   */
138  private final class PostOrderIterator extends UnmodifiableIterator<T> {
139    private final Deque<T> stack;
140    private final BitSet hasExpanded;
141
142    PostOrderIterator(T root) {
143      this.stack = new ArrayDeque<T>(8);
144      stack.addLast(root);
145      this.hasExpanded = new BitSet();
146    }
147
148    @Override
149    public boolean hasNext() {
150      return !stack.isEmpty();
151    }
152
153    @Override
154    public T next() {
155      while (true) {
156        T node = stack.getLast();
157        boolean expandedNode = hasExpanded.get(stack.size() - 1);
158        if (expandedNode) {
159          stack.removeLast();
160          hasExpanded.clear(stack.size());
161          return node;
162        } else {
163          hasExpanded.set(stack.size() - 1);
164          pushIfPresent(stack, rightChild(node));
165          pushIfPresent(stack, leftChild(node));
166        }
167      }
168    }
169  }
170
171  // TODO(lowasser): see if any significant optimizations are possible for breadthFirstIterator
172
173  public final FluentIterable<T> inOrderTraversal(final T root) {
174    checkNotNull(root);
175    return new FluentIterable<T>() {
176      @Override
177      public UnmodifiableIterator<T> iterator() {
178        return new InOrderIterator(root);
179      }
180    };
181  }
182
183  private final class InOrderIterator extends AbstractIterator<T> {
184    private final Deque<T> stack;
185    private final BitSet hasExpandedLeft;
186
187    InOrderIterator(T root) {
188      this.stack = new ArrayDeque<T>(8);
189      this.hasExpandedLeft = new BitSet();
190      stack.addLast(root);
191    }
192
193    @Override
194    protected T computeNext() {
195      while (!stack.isEmpty()) {
196        T node = stack.getLast();
197        if (hasExpandedLeft.get(stack.size() - 1)) {
198          stack.removeLast();
199          hasExpandedLeft.clear(stack.size());
200          pushIfPresent(stack, rightChild(node));
201          return node;
202        } else {
203          hasExpandedLeft.set(stack.size() - 1);
204          pushIfPresent(stack, leftChild(node));
205        }
206      }
207      return endOfData();
208    }
209  }
210
211  private static <T> void pushIfPresent(Deque<T> stack, Optional<T> node) {
212    if (node.isPresent()) {
213      stack.addLast(node.get());
214    }
215  }
216}