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

com.graphhopper.routing.util.TarjansSCCAlgorithm Maven / Gradle / Ivy

/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for 
 *  additional information regarding copyright ownership.
 * 
 *  GraphHopper GmbH licenses this file to you under the Apache License, 
 *  Version 2.0 (the "License"); you may not use this file except in 
 *  compliance with the License. You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.graphhopper.routing.util;

import com.graphhopper.coll.GHBitSet;
import com.graphhopper.coll.GHBitSetImpl;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.util.EdgeIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.stack.array.TIntArrayStack;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * Implementation of Tarjan's algorithm using an explicit stack. The traditional recursive approach
 * runs into stack overflow pretty quickly. The algorithm is used within GraphHopper to find
 * strongly connected components to detect dead-ends leading to routes not found.
 * 

* See http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm. See * http://www.timl.id.au/?p=327 and http://homepages.ecs.vuw.ac.nz/~djp/files/P05.pdf */ public class TarjansSCCAlgorithm { private final ArrayList components = new ArrayList(); private final GraphHopperStorage graph; private final TIntArrayStack nodeStack; private final GHBitSet onStack; private final GHBitSet ignoreSet; private final int[] nodeIndex; private final int[] nodeLowLink; private int index = 1; private final EdgeFilter edgeFilter; public TarjansSCCAlgorithm( GraphHopperStorage graph, GHBitSet ignoreSet, final EdgeFilter edgeFilter ) { this.graph = graph; this.nodeStack = new TIntArrayStack(); this.onStack = new GHBitSetImpl(graph.getNodes()); this.nodeIndex = new int[graph.getNodes()]; this.nodeLowLink = new int[graph.getNodes()]; this.edgeFilter = edgeFilter; this.ignoreSet = ignoreSet; } /** * Find and return list of all strongly connected components in g. */ public List findComponents() { int nodes = graph.getNodes(); for (int start = 0; start < nodes; start++) { if (nodeIndex[start] == 0 && !ignoreSet.contains(start) && !graph.isNodeRemoved(start)) strongConnect(start); } return components; } /** * Find all components reachable from firstNode, add them to 'components' *

* @param firstNode start search of SCC at this node */ private void strongConnect( int firstNode ) { final Stack stateStack = new Stack(); stateStack.push(TarjanState.startState(firstNode)); // nextState label is equivalent to the function entry point in the recursive Tarjan's algorithm. nextState: while (!stateStack.empty()) { TarjanState state = stateStack.pop(); final int start = state.start; final EdgeIterator iter; if (state.isStart()) { // We're traversing a new node 'start'. Set the depth index for this node to the smallest unused index. nodeIndex[start] = index; nodeLowLink[start] = index; index++; nodeStack.push(start); onStack.add(start); iter = graph.createEdgeExplorer(edgeFilter).setBaseNode(start); } else { // We're resuming iteration over the next child of 'start', set lowLink as appropriate. iter = state.iter; int prevConnectedId = iter.getAdjNode(); nodeLowLink[start] = Math.min(nodeLowLink[start], nodeLowLink[prevConnectedId]); } // Each element (excluding the first) in the current component should be able to find // a successor with a lower nodeLowLink. while (iter.next()) { int connectedId = iter.getAdjNode(); if (ignoreSet.contains(start)) continue; if (nodeIndex[connectedId] == 0) { // Push resume and start states onto state stack to continue our DFS through the graph after the jump. // Ideally we'd just call strongConnectIterative(connectedId); stateStack.push(TarjanState.resumeState(start, iter)); stateStack.push(TarjanState.startState(connectedId)); continue nextState; } else if (onStack.contains(connectedId)) { nodeLowLink[start] = Math.min(nodeLowLink[start], nodeIndex[connectedId]); } } // If nodeLowLink == nodeIndex, then we are the first element in a component. // Add all nodes higher up on nodeStack to this component. if (nodeIndex[start] == nodeLowLink[start]) { TIntArrayList component = new TIntArrayList(); int node; while ((node = nodeStack.pop()) != start) { component.add(node); onStack.remove(node); } component.add(start); component.trimToSize(); onStack.remove(start); components.add(component); } } } /** * Internal stack state of algorithm, used to avoid recursive function calls and hitting stack * overflow exceptions. State is either 'start' for new nodes or 'resume' for partially * traversed nodes. */ private static class TarjanState { final int start; final EdgeIterator iter; // Iterator only present in 'resume' state. boolean isStart() { return iter == null; } private TarjanState( final int start, final EdgeIterator iter ) { this.start = start; this.iter = iter; } public static TarjanState startState( int start ) { return new TarjanState(start, null); } public static TarjanState resumeState( int start, EdgeIterator iter ) { return new TarjanState(start, iter); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy