001/*
002 * Copyright (C) 2017 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.graph;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021
022import com.google.common.annotations.Beta;
023import com.google.common.collect.AbstractIterator;
024import com.google.common.collect.ImmutableSet;
025import com.google.errorprone.annotations.DoNotMock;
026import java.util.ArrayDeque;
027import java.util.Deque;
028import java.util.HashSet;
029import java.util.Iterator;
030import java.util.Set;
031import org.checkerframework.checker.nullness.compatqual.NullableDecl;
032
033/**
034 * An object that can traverse the nodes that are reachable from a specified (set of) start node(s)
035 * using a specified {@link SuccessorsFunction}.
036 *
037 * <p>There are two entry points for creating a {@code Traverser}: {@link
038 * #forTree(SuccessorsFunction)} and {@link #forGraph(SuccessorsFunction)}. You should choose one
039 * based on your answers to the following questions:
040 *
041 * <ol>
042 *   <li>Is there only one path to any node that's reachable from any start node? (If so, the graph
043 *       to be traversed is a tree or forest even if it is a subgraph of a graph which is neither.)
044 *   <li>Are the node objects' implementations of {@code equals()}/{@code hashCode()} <a
045 *       href="https://github.com/google/guava/wiki/GraphsExplained#non-recursiveness">recursive</a>?
046 * </ol>
047 *
048 * <p>If your answers are:
049 *
050 * <ul>
051 *   <li>(1) "no" and (2) "no", use {@link #forGraph(SuccessorsFunction)}.
052 *   <li>(1) "yes" and (2) "yes", use {@link #forTree(SuccessorsFunction)}.
053 *   <li>(1) "yes" and (2) "no", you can use either, but {@code forTree()} will be more efficient.
054 *   <li>(1) "no" and (2) "yes", <b><i>neither will work</i></b>, but if you transform your node
055 *       objects into a non-recursive form, you can use {@code forGraph()}.
056 * </ul>
057 *
058 * @author Jens Nyman
059 * @param <N> Node parameter type
060 * @since 23.1
061 */
062@Beta
063@DoNotMock(
064    "Call forGraph or forTree, passing a lambda or a Graph with the desired edges (built with"
065        + " GraphBuilder)")
066public abstract class Traverser<N> {
067  private final SuccessorsFunction<N> successorFunction;
068
069  private Traverser(SuccessorsFunction<N> successorFunction) {
070    this.successorFunction = checkNotNull(successorFunction);
071  }
072
073  /**
074   * Creates a new traverser for the given general {@code graph}.
075   *
076   * <p>Traversers created using this method are guaranteed to visit each node reachable from the
077   * start node(s) at most once.
078   *
079   * <p>If you know that no node in {@code graph} is reachable by more than one path from the start
080   * node(s), consider using {@link #forTree(SuccessorsFunction)} instead.
081   *
082   * <p><b>Performance notes</b>
083   *
084   * <ul>
085   *   <li>Traversals require <i>O(n)</i> time (where <i>n</i> is the number of nodes reachable from
086   *       the start node), assuming that the node objects have <i>O(1)</i> {@code equals()} and
087   *       {@code hashCode()} implementations. (See the <a
088   *       href="https://github.com/google/guava/wiki/GraphsExplained#elements-must-be-useable-as-map-keys">
089   *       notes on element objects</a> for more information.)
090   *   <li>While traversing, the traverser will use <i>O(n)</i> space (where <i>n</i> is the number
091   *       of nodes that have thus far been visited), plus <i>O(H)</i> space (where <i>H</i> is the
092   *       number of nodes that have been seen but not yet visited, that is, the "horizon").
093   * </ul>
094   *
095   * @param graph {@link SuccessorsFunction} representing a general graph that may have cycles.
096   */
097  public static <N> Traverser<N> forGraph(final SuccessorsFunction<N> graph) {
098    return new Traverser<N>(graph) {
099      @Override
100      Traversal<N> newTraversal() {
101        return Traversal.inGraph(graph);
102      }
103    };
104  }
105
106  /**
107   * Creates a new traverser for a directed acyclic graph that has at most one path from the start
108   * node(s) to any node reachable from the start node(s), and has no paths from any start node to
109   * any other start node, such as a tree or forest.
110   *
111   * <p>{@code forTree()} is especially useful (versus {@code forGraph()}) in cases where the data
112   * structure being traversed is, in addition to being a tree/forest, also defined <a
113   * href="https://github.com/google/guava/wiki/GraphsExplained#non-recursiveness">recursively</a>.
114   * This is because the {@code forTree()}-based implementations don't keep track of visited nodes,
115   * and therefore don't need to call `equals()` or `hashCode()` on the node objects; this saves
116   * both time and space versus traversing the same graph using {@code forGraph()}.
117   *
118   * <p>Providing a graph to be traversed for which there is more than one path from the start
119   * node(s) to any node may lead to:
120   *
121   * <ul>
122   *   <li>Traversal not terminating (if the graph has cycles)
123   *   <li>Nodes being visited multiple times (if multiple paths exist from any start node to any
124   *       node reachable from any start node)
125   * </ul>
126   *
127   * <p><b>Performance notes</b>
128   *
129   * <ul>
130   *   <li>Traversals require <i>O(n)</i> time (where <i>n</i> is the number of nodes reachable from
131   *       the start node).
132   *   <li>While traversing, the traverser will use <i>O(H)</i> space (where <i>H</i> is the number
133   *       of nodes that have been seen but not yet visited, that is, the "horizon").
134   * </ul>
135   *
136   * <p><b>Examples</b> (all edges are directed facing downwards)
137   *
138   * <p>The graph below would be valid input with start nodes of {@code a, f, c}. However, if {@code
139   * b} were <i>also</i> a start node, then there would be multiple paths to reach {@code e} and
140   * {@code h}.
141   *
142   * <pre>{@code
143   *    a     b      c
144   *   / \   / \     |
145   *  /   \ /   \    |
146   * d     e     f   g
147   *       |
148   *       |
149   *       h
150   * }</pre>
151   *
152   * <p>.
153   *
154   * <p>The graph below would be a valid input with start nodes of {@code a, f}. However, if {@code
155   * b} were a start node, there would be multiple paths to {@code f}.
156   *
157   * <pre>{@code
158   *    a     b
159   *   / \   / \
160   *  /   \ /   \
161   * c     d     e
162   *        \   /
163   *         \ /
164   *          f
165   * }</pre>
166   *
167   * <p><b>Note on binary trees</b>
168   *
169   * <p>This method can be used to traverse over a binary tree. Given methods {@code
170   * leftChild(node)} and {@code rightChild(node)}, this method can be called as
171   *
172   * <pre>{@code
173   * Traverser.forTree(node -> ImmutableList.of(leftChild(node), rightChild(node)));
174   * }</pre>
175   *
176   * @param tree {@link SuccessorsFunction} representing a directed acyclic graph that has at most
177   *     one path between any two nodes
178   */
179  public static <N> Traverser<N> forTree(final SuccessorsFunction<N> tree) {
180    if (tree instanceof BaseGraph) {
181      checkArgument(((BaseGraph<?>) tree).isDirected(), "Undirected graphs can never be trees.");
182    }
183    if (tree instanceof Network) {
184      checkArgument(((Network<?, ?>) tree).isDirected(), "Undirected networks can never be trees.");
185    }
186    return new Traverser<N>(tree) {
187      @Override
188      Traversal<N> newTraversal() {
189        return Traversal.inTree(tree);
190      }
191    };
192  }
193
194  /**
195   * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
196   * the order of a breadth-first traversal. That is, all the nodes of depth 0 are returned, then
197   * depth 1, then 2, and so on.
198   *
199   * <p><b>Example:</b> The following graph with {@code startNode} {@code a} would return nodes in
200   * the order {@code abcdef} (assuming successors are returned in alphabetical order).
201   *
202   * <pre>{@code
203   * b ---- a ---- d
204   * |      |
205   * |      |
206   * e ---- c ---- f
207   * }</pre>
208   *
209   * <p>The behavior of this method is undefined if the nodes, or the topology of the graph, change
210   * while iteration is in progress.
211   *
212   * <p>The returned {@code Iterable} can be iterated over multiple times. Every iterator will
213   * compute its next element on the fly. It is thus possible to limit the traversal to a certain
214   * number of nodes as follows:
215   *
216   * <pre>{@code
217   * Iterables.limit(Traverser.forGraph(graph).breadthFirst(node), maxNumberOfNodes);
218   * }</pre>
219   *
220   * <p>See <a href="https://en.wikipedia.org/wiki/Breadth-first_search">Wikipedia</a> for more
221   * info.
222   *
223   * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
224   */
225  public final Iterable<N> breadthFirst(N startNode) {
226    return breadthFirst(ImmutableSet.of(startNode));
227  }
228
229  /**
230   * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
231   * startNodes}, in the order of a breadth-first traversal. This is equivalent to a breadth-first
232   * traversal of a graph with an additional root node whose successors are the listed {@code
233   * startNodes}.
234   *
235   * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph
236   * @see #breadthFirst(Object)
237   * @since 24.1
238   */
239  public final Iterable<N> breadthFirst(Iterable<? extends N> startNodes) {
240    final ImmutableSet<N> validated = validate(startNodes);
241    return new Iterable<N>() {
242      @Override
243      public Iterator<N> iterator() {
244        return newTraversal().breadthFirst(validated.iterator());
245      }
246    };
247  }
248
249  /**
250   * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
251   * the order of a depth-first pre-order traversal. "Pre-order" implies that nodes appear in the
252   * {@code Iterable} in the order in which they are first visited.
253   *
254   * <p><b>Example:</b> The following graph with {@code startNode} {@code a} would return nodes in
255   * the order {@code abecfd} (assuming successors are returned in alphabetical order).
256   *
257   * <pre>{@code
258   * b ---- a ---- d
259   * |      |
260   * |      |
261   * e ---- c ---- f
262   * }</pre>
263   *
264   * <p>The behavior of this method is undefined if the nodes, or the topology of the graph, change
265   * while iteration is in progress.
266   *
267   * <p>The returned {@code Iterable} can be iterated over multiple times. Every iterator will
268   * compute its next element on the fly. It is thus possible to limit the traversal to a certain
269   * number of nodes as follows:
270   *
271   * <pre>{@code
272   * Iterables.limit(
273   *     Traverser.forGraph(graph).depthFirstPreOrder(node), maxNumberOfNodes);
274   * }</pre>
275   *
276   * <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info.
277   *
278   * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
279   */
280  public final Iterable<N> depthFirstPreOrder(N startNode) {
281    return depthFirstPreOrder(ImmutableSet.of(startNode));
282  }
283
284  /**
285   * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
286   * startNodes}, in the order of a depth-first pre-order traversal. This is equivalent to a
287   * depth-first pre-order traversal of a graph with an additional root node whose successors are
288   * the listed {@code startNodes}.
289   *
290   * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph
291   * @see #depthFirstPreOrder(Object)
292   * @since 24.1
293   */
294  public final Iterable<N> depthFirstPreOrder(Iterable<? extends N> startNodes) {
295    final ImmutableSet<N> validated = validate(startNodes);
296    return new Iterable<N>() {
297      @Override
298      public Iterator<N> iterator() {
299        return newTraversal().preOrder(validated.iterator());
300      }
301    };
302  }
303
304  /**
305   * Returns an unmodifiable {@code Iterable} over the nodes reachable from {@code startNode}, in
306   * the order of a depth-first post-order traversal. "Post-order" implies that nodes appear in the
307   * {@code Iterable} in the order in which they are visited for the last time.
308   *
309   * <p><b>Example:</b> The following graph with {@code startNode} {@code a} would return nodes in
310   * the order {@code fcebda} (assuming successors are returned in alphabetical order).
311   *
312   * <pre>{@code
313   * b ---- a ---- d
314   * |      |
315   * |      |
316   * e ---- c ---- f
317   * }</pre>
318   *
319   * <p>The behavior of this method is undefined if the nodes, or the topology of the graph, change
320   * while iteration is in progress.
321   *
322   * <p>The returned {@code Iterable} can be iterated over multiple times. Every iterator will
323   * compute its next element on the fly. It is thus possible to limit the traversal to a certain
324   * number of nodes as follows:
325   *
326   * <pre>{@code
327   * Iterables.limit(
328   *     Traverser.forGraph(graph).depthFirstPostOrder(node), maxNumberOfNodes);
329   * }</pre>
330   *
331   * <p>See <a href="https://en.wikipedia.org/wiki/Depth-first_search">Wikipedia</a> for more info.
332   *
333   * @throws IllegalArgumentException if {@code startNode} is not an element of the graph
334   */
335  public final Iterable<N> depthFirstPostOrder(N startNode) {
336    return depthFirstPostOrder(ImmutableSet.of(startNode));
337  }
338
339  /**
340   * Returns an unmodifiable {@code Iterable} over the nodes reachable from any of the {@code
341   * startNodes}, in the order of a depth-first post-order traversal. This is equivalent to a
342   * depth-first post-order traversal of a graph with an additional root node whose successors are
343   * the listed {@code startNodes}.
344   *
345   * @throws IllegalArgumentException if any of {@code startNodes} is not an element of the graph
346   * @see #depthFirstPostOrder(Object)
347   * @since 24.1
348   */
349  public final Iterable<N> depthFirstPostOrder(Iterable<? extends N> startNodes) {
350    final ImmutableSet<N> validated = validate(startNodes);
351    return new Iterable<N>() {
352      @Override
353      public Iterator<N> iterator() {
354        return newTraversal().postOrder(validated.iterator());
355      }
356    };
357  }
358
359  abstract Traversal<N> newTraversal();
360
361  @SuppressWarnings("CheckReturnValue")
362  private ImmutableSet<N> validate(Iterable<? extends N> startNodes) {
363    ImmutableSet<N> copy = ImmutableSet.copyOf(startNodes);
364    for (N node : copy) {
365      successorFunction.successors(node); // Will throw if node doesn't exist
366    }
367    return copy;
368  }
369
370  /**
371   * Abstracts away the difference between traversing a graph vs. a tree. For a tree, we just take
372   * the next element from the next non-empty iterator; for graph, we need to loop through the next
373   * non-empty iterator to find first unvisited node.
374   */
375  private abstract static class Traversal<N> {
376    final SuccessorsFunction<N> successorFunction;
377
378    Traversal(SuccessorsFunction<N> successorFunction) {
379      this.successorFunction = successorFunction;
380    }
381
382    static <N> Traversal<N> inGraph(SuccessorsFunction<N> graph) {
383      final Set<N> visited = new HashSet<>();
384      return new Traversal<N>(graph) {
385        @Override
386        N visitNext(Deque<Iterator<? extends N>> horizon) {
387          Iterator<? extends N> top = horizon.getFirst();
388          while (top.hasNext()) {
389            N element = checkNotNull(top.next());
390            if (visited.add(element)) {
391              return element;
392            }
393          }
394          horizon.removeFirst();
395          return null;
396        }
397      };
398    }
399
400    static <N> Traversal<N> inTree(SuccessorsFunction<N> tree) {
401      return new Traversal<N>(tree) {
402        @Override
403        N visitNext(Deque<Iterator<? extends N>> horizon) {
404          Iterator<? extends N> top = horizon.getFirst();
405          if (top.hasNext()) {
406            return checkNotNull(top.next());
407          }
408          horizon.removeFirst();
409          return null;
410        }
411      };
412    }
413
414    final Iterator<N> breadthFirst(Iterator<? extends N> startNodes) {
415      return topDown(startNodes, InsertionOrder.BACK);
416    }
417
418    final Iterator<N> preOrder(Iterator<? extends N> startNodes) {
419      return topDown(startNodes, InsertionOrder.FRONT);
420    }
421
422    /**
423     * In top-down traversal, an ancestor node is always traversed before any of its descendant
424     * nodes. The traversal order among descendant nodes (particularly aunts and nieces) are
425     * determined by the {@code InsertionOrder} parameter: nieces are placed at the FRONT before
426     * aunts for pre-order; while in BFS they are placed at the BACK after aunts.
427     */
428    private Iterator<N> topDown(Iterator<? extends N> startNodes, final InsertionOrder order) {
429      final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>();
430      horizon.add(startNodes);
431      return new AbstractIterator<N>() {
432        @Override
433        protected N computeNext() {
434          do {
435            N next = visitNext(horizon);
436            if (next != null) {
437              Iterator<? extends N> successors = successorFunction.successors(next).iterator();
438              if (successors.hasNext()) {
439                // BFS: horizon.addLast(successors)
440                // Pre-order: horizon.addFirst(successors)
441                order.insertInto(horizon, successors);
442              }
443              return next;
444            }
445          } while (!horizon.isEmpty());
446          return endOfData();
447        }
448      };
449    }
450
451    final Iterator<N> postOrder(Iterator<? extends N> startNodes) {
452      final Deque<N> ancestorStack = new ArrayDeque<>();
453      final Deque<Iterator<? extends N>> horizon = new ArrayDeque<>();
454      horizon.add(startNodes);
455      return new AbstractIterator<N>() {
456        @Override
457        protected N computeNext() {
458          for (N next = visitNext(horizon); next != null; next = visitNext(horizon)) {
459            Iterator<? extends N> successors = successorFunction.successors(next).iterator();
460            if (!successors.hasNext()) {
461              return next;
462            }
463            horizon.addFirst(successors);
464            ancestorStack.push(next);
465          }
466          return ancestorStack.isEmpty() ? endOfData() : ancestorStack.pop();
467        }
468      };
469    }
470
471    /**
472     * Visits the next node from the top iterator of {@code horizon} and returns the visited node.
473     * Null is returned to indicate reaching the end of the top iterator.
474     *
475     * <p>For example, if horizon is {@code [[a, b], [c, d], [e]]}, {@code visitNext()} will return
476     * {@code [a, b, null, c, d, null, e, null]} sequentially, encoding the topological structure.
477     * (Note, however, that the callers of {@code visitNext()} often insert additional iterators
478     * into {@code horizon} between calls to {@code visitNext()}. This causes them to receive
479     * additional values interleaved with those shown above.)
480     */
481    @NullableDecl
482    abstract N visitNext(Deque<Iterator<? extends N>> horizon);
483  }
484
485  /** Poor man's method reference for {@code Deque::addFirst} and {@code Deque::addLast}. */
486  private enum InsertionOrder {
487    FRONT {
488      @Override
489      <T> void insertInto(Deque<T> deque, T value) {
490        deque.addFirst(value);
491      }
492    },
493    BACK {
494      @Override
495      <T> void insertInto(Deque<T> deque, T value) {
496        deque.addLast(value);
497      }
498    };
499
500    abstract <T> void insertInto(Deque<T> deque, T value);
501  }
502}