diff --git a/src/TopologicalSort.php b/src/TopologicalSort.php index 839c7a2..7ce48de 100644 --- a/src/TopologicalSort.php +++ b/src/TopologicalSort.php @@ -19,47 +19,56 @@ class TopologicalSort extends BaseGraph /** * run algorithm and return an ordered/sorted set of Vertices * - * the topologic sorting may be non-unique depending on your edges. this - * algorithm tries to keep the order of vertices as added to the graph in - * this case. + * the topological sorting may be non-unique depending on your edges * * @return Vertices */ public function getVertices() { - $tsl = array(); + $stack = array(); // visited nodes with unvisited children $visited = array(); + $output = array(); - // TODO: find alternative to recursive algorithm to avoid hitting recursion limit with ~100 nodes + /** @var Vertex $top */ // TODO: avoid having to reverse all vertices multiple times + // pick a node to examine next - assume there are isolated nodes + foreach (array_reverse($this->graph->getVertices()->getVector()) as $top) { + $tid = $top->getId(); + if (!isset($visited[$tid])) { // don't examine if already found + array_push($stack, $top); + } - foreach(array_reverse($this->graph->getVertices()->getVector()) as $vertex) { - $this->visit($vertex, $visited, $tsl); - } + while ($stack) { + /** @var Vertex $current */ + $node = end($stack); + $nid = $node->getId(); - return new Vertices(array_reverse($tsl, true)); - } + $visited[$nid] = false; // temporary mark + $found = false; // new children found during visit to this node - protected function visit(Vertex $vertex, array &$visited, array &$tsl) - { - $vid = $vertex->getId(); - if (isset($visited[$vid])) { - if ($visited[$vid] === false) { - // temporary mark => not a DAG - throw new UnexpectedValueException('Not a DAG'); - } - // otherwise already marked/visisted => no need to check again - } else { - // temporary mark - $visited[$vid] = false; + // find the next node to visit + /** @var Vertex $child */ + foreach (array_reverse($node->getVerticesEdgeTo()->getVector()) as $child) { + $cid = $child->getId(); + if (!isset($visited[$cid])) { + // found a new node - push it onto the stack + array_push($stack, $child); + $found = true; // move onto the new node + break; + } else if ($visited[$cid] === false) { + // temporary mark => not a DAG + throw new UnexpectedValueException('Not a DAG'); + } + } - foreach (array_reverse($vertex->getVerticesEdgeTo()->getVector()) as $v) { - $this->visit($v, $visited, $tsl); + if (!$found) { + array_pop($stack); // no new children found - we're done with this node + $visited[$nid] = true; // mark as visited + array_push($output, $node); // add to results + } } - - // mark as visited and include in result - $visited[$vid] = true; - $tsl[$vid] = $vertex; } + + return new Vertices(array_reverse($output, true)); } }