All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.traverse.DepthFirstIterator Maven / Gradle / Ivy

/*
 * (C) Copyright 2003-2021, by Liviu Rau and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.traverse;

import org.jgrapht.*;
import org.jgrapht.util.*;

import java.util.*;

/**
 * A depth-first iterator for a directed or undirected graph.
 * 
 * 

* For this iterator to work correctly the graph must not be modified during iteration. Currently * there are no means to ensure that, nor to fail-fast. The results of such modifications are * undefined. * * @param the graph vertex type * @param the graph edge type * * @author Liviu Rau * @author Barak Naveh */ public class DepthFirstIterator extends CrossComponentIterator { /** * Sentinel object. Unfortunately, we can't use null, because ArrayDeque won't accept those. And * we don't want to rely on the caller to provide a sentinel object for us. So we have to play * typecasting games. */ public static final Object SENTINEL = new Object(); /** * Standard vertex visit state enumeration. */ protected static enum VisitColor { /** * Vertex has not been returned via iterator yet. */ WHITE, /** * Vertex has been returned via iterator, but we're not done with all of its out-edges yet. */ GRAY, /** * Vertex has been returned via iterator, and we're done with all of its out-edges. */ BLACK } private Deque stack = new ArrayDeque<>(); /** * Creates a new depth-first iterator for the specified graph. * * @param g the graph to be iterated. */ public DepthFirstIterator(Graph g) { this(g, (V) null); } /** * Creates a new depth-first iterator for the specified graph. Iteration will start at the * specified start vertex and will be limited to the connected component that includes that * vertex. If the specified start vertex is null, iteration will start at an * arbitrary vertex and will not be limited, that is, will be able to traverse all the graph. * * @param g the graph to be iterated. * @param startVertex the vertex iteration to be started. */ public DepthFirstIterator(Graph g, V startVertex) { super(g, startVertex); } /** * Creates a new depth-first iterator for the specified graph. Iteration will start at the * specified start vertices and will be limited to the connected component that includes those * vertices. If the specified start vertices is null, iteration will start at an * arbitrary vertex and will not be limited, that is, will be able to traverse all the graph. * * @param g the graph to be iterated. * @param startVertices the vertices iteration to be started. */ public DepthFirstIterator(Graph g, Iterable startVertices) { super(g, startVertices); } @Override protected boolean isConnectedComponentExhausted() { for (;;) { if (stack.isEmpty()) { return true; } if (stack.getLast() != SENTINEL) { // Found a non-sentinel. return false; } // Found a sentinel: pop it, record the finish time, // and then loop to check the rest of the stack. // Pop null we peeked at above. stack.removeLast(); // This will pop corresponding vertex to be recorded as finished. recordFinish(); } } @Override protected void encounterVertex(V vertex, E edge) { putSeenData(vertex, VisitColor.WHITE); stack.addLast(vertex); } @Override protected void encounterVertexAgain(V vertex, E edge) { VisitColor color = getSeenData(vertex); if (color != VisitColor.WHITE) { // We've already visited this vertex; no need to mess with the // stack (either it's BLACK and not there at all, or it's GRAY // and therefore just a sentinel). return; } // Since we've encountered it before, and it's still WHITE, it // *must* be on the stack. Use removeLastOccurrence on the // assumption that for typical topologies and traversals, // it's likely to be nearer the top of the stack than // the bottom of the stack. boolean found = stack.removeLastOccurrence(vertex); assert (found); stack.addLast(vertex); } @Override protected V provideNextVertex() { V v; for (;;) { Object o = stack.removeLast(); if (o == SENTINEL) { // This is a finish-time sentinel we previously pushed. recordFinish(); // Now carry on with another pop until we find a non-sentinel } else { // Got a real vertex to start working on v = TypeUtil.uncheckedCast(o); break; } } // Push a sentinel for v onto the stack so that we'll know // when we're done with it. stack.addLast(v); stack.addLast(SENTINEL); putSeenData(v, VisitColor.GRAY); return v; } private void recordFinish() { V v = TypeUtil.uncheckedCast(stack.removeLast()); putSeenData(v, VisitColor.BLACK); finishVertex(v); } /** * Retrieves the LIFO stack of vertices which have been encountered but not yet visited (WHITE). * This stack also contains sentinel entries representing vertices which have been * visited but are still GRAY. A sentinel entry is a sequence (v, SENTINEL), whereas a * non-sentinel entry is just (v). * * @return stack */ public Deque getStack() { return stack; } }