/** * Finds connected components in the provided directed graph. * * @param DirectedGraph $graph * The DirectedGraph to search for connected components. * @param TarjanSCCVisitor $visitor * The visitor that will collect and store the connected components. One * will be created if not provided. * * @return TarjanSCCVisitor * The finalized visitor. */ public static function tarjan_scc(DirectedGraph $graph, TarjanSCCVisitor $visitor = NULL) { $visitor = $visitor ?: new TarjanSCCVisitor(); $counter = 0; $stack = array(); $indices = new \SplObjectStorage(); $lowlimits = new \SplObjectStorage(); $visit = function ($vertex) use(&$visit, &$counter, $graph, &$stack, $indices, $lowlimits, $visitor) { $indices->attach($vertex, $counter); $lowlimits->attach($vertex, $counter); $stack[] = $vertex; $counter++; $graph->eachAdjacent($vertex, function ($to) use(&$visit, $vertex, $indices, $lowlimits, &$stack) { if (!$indices->contains($to)) { $visit($to); $lowlimits[$vertex] = min($lowlimits[$vertex], $lowlimits[$to]); } else { if (in_array($to, $stack)) { $lowlimits[$vertex] = min($lowlimits[$vertex], $indices[$to]); } } }); if ($lowlimits[$vertex] === $indices[$vertex]) { $visitor->newComponent(); do { $other = array_pop($stack); $visitor->addToCurrentComponent($other); } while ($other != $vertex); } }; $graph->eachVertex(function ($vertex) use(&$visit, $indices) { if (!$indices->contains($vertex)) { $visit($vertex); } }); return $visitor; }
/** * Finds source vertices in a DirectedGraph, then enqueues them. * * @param DirectedGraph $graph * @param DepthFirstVisitorInterface $visitor * * @return \SplQueue */ public static function find_sources(DirectedGraph $graph, DepthFirstVisitorInterface $visitor) { $incomings = new \SplObjectStorage(); $queue = new \SplQueue(); $graph->eachEdge(function ($edge) use(&$incomings) { if (!isset($incomings[$edge[1]])) { $incomings[$edge[1]] = new \SplObjectStorage(); } $incomings[$edge[1]]->attach($edge[0]); }); // Prime the queue with vertices that have no incoming edges. $graph->eachVertex(function ($vertex) use($queue, $incomings, $visitor) { if (!$incomings->contains($vertex)) { $queue->push($vertex); // TRUE second param indicates source vertex $visitor->onInitializeVertex($vertex, TRUE, $queue); } else { $visitor->onInitializeVertex($vertex, FALSE, $queue); } }); return $queue; }