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  /** Returns the children of this node, in left-to-right order. */
059  @Override
060  public final Iterable<T> children(final T root) {
061    checkNotNull(root);
062    return new FluentIterable<T>() {
063      @Override
064      public Iterator<T> iterator() {
065        return new AbstractIterator<T>() {
066          boolean doneLeft;
067          boolean doneRight;
068
069          @Override
070          protected T computeNext() {
071            if (!doneLeft) {
072              doneLeft = true;
073              Optional<T> left = leftChild(root);
074              if (left.isPresent()) {
075                return left.get();
076              }
077            }
078            if (!doneRight) {
079              doneRight = true;
080              Optional<T> right = rightChild(root);
081              if (right.isPresent()) {
082                return right.get();
083              }
084            }
085            return endOfData();
086          }
087        };
088      }
089    };
090  }
091
092  @Override
093  UnmodifiableIterator<T> preOrderIterator(T root) {
094    return new PreOrderIterator(root);
095  }
096
097  /*
098   * Optimized implementation of preOrderIterator for binary trees.
099   */
100  private final class PreOrderIterator extends UnmodifiableIterator<T>
101      implements PeekingIterator<T> {
102    private final Deque<T> stack;
103
104    PreOrderIterator(T root) {
105      this.stack = new ArrayDeque<T>(8);
106      stack.addLast(root);
107    }
108
109    @Override
110    public boolean hasNext() {
111      return !stack.isEmpty();
112    }
113
114    @Override
115    public T next() {
116      T result = stack.removeLast();
117      pushIfPresent(stack, rightChild(result));
118      pushIfPresent(stack, leftChild(result));
119      return result;
120    }
121
122    @Override
123    public T peek() {
124      return stack.getLast();
125    }
126  }
127
128  @Override
129  UnmodifiableIterator<T> postOrderIterator(T root) {
130    return new PostOrderIterator(root);
131  }
132
133  /*
134   * Optimized implementation of postOrderIterator for binary trees.
135   */
136  private final class PostOrderIterator extends UnmodifiableIterator<T> {
137    private final Deque<T> stack;
138    private final BitSet hasExpanded;
139
140    PostOrderIterator(T root) {
141      this.stack = new ArrayDeque<T>(8);
142      stack.addLast(root);
143      this.hasExpanded = new BitSet();
144    }
145
146    @Override
147    public boolean hasNext() {
148      return !stack.isEmpty();
149    }
150
151    @Override
152    public T next() {
153      while (true) {
154        T node = stack.getLast();
155        boolean expandedNode = hasExpanded.get(stack.size() - 1);
156        if (expandedNode) {
157          stack.removeLast();
158          hasExpanded.clear(stack.size());
159          return node;
160        } else {
161          hasExpanded.set(stack.size() - 1);
162          pushIfPresent(stack, rightChild(node));
163          pushIfPresent(stack, leftChild(node));
164        }
165      }
166    }
167  }
168
169  // TODO(lowasser): see if any significant optimizations are possible for breadthFirstIterator
170
171  public final FluentIterable<T> inOrderTraversal(final T root) {
172    checkNotNull(root);
173    return new FluentIterable<T>() {
174      @Override
175      public UnmodifiableIterator<T> iterator() {
176        return new InOrderIterator(root);
177      }
178    };
179  }
180
181  private final class InOrderIterator extends AbstractIterator<T> {
182    private final Deque<T> stack;
183    private final BitSet hasExpandedLeft;
184
185    InOrderIterator(T root) {
186      this.stack = new ArrayDeque<T>(8);
187      this.hasExpandedLeft = new BitSet();
188      stack.addLast(root);
189    }
190
191    @Override
192    protected T computeNext() {
193      while (!stack.isEmpty()) {
194        T node = stack.getLast();
195        if (hasExpandedLeft.get(stack.size() - 1)) {
196          stack.removeLast();
197          hasExpandedLeft.clear(stack.size());
198          pushIfPresent(stack, rightChild(node));
199          return node;
200        } else {
201          hasExpandedLeft.set(stack.size() - 1);
202          pushIfPresent(stack, leftChild(node));
203        }
204      }
205      return endOfData();
206    }
207  }
208
209  private static <T> void pushIfPresent(Deque<T> stack, Optional<T> node) {
210    if (node.isPresent()) {
211      stack.addLast(node.get());
212    }
213  }
214}