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