Skip to content

Commit

Permalink
Replace recursive topological sort with iterative algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
phyrwork committed Jul 23, 2017
1 parent 81db404 commit aaca6a5
Showing 1 changed file with 37 additions and 28 deletions.
65 changes: 37 additions & 28 deletions src/TopologicalSort.php
Original file line number Diff line number Diff line change
Expand Up @@ -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

This comment has been minimized.

Copy link
@Gillu13

Gillu13 Jan 28, 2020

Hi!

Just for my information, why do you reverse the vertices? Is that because you want to loosely keep the order at which they were added to the graph?

// 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));
}
}

0 comments on commit aaca6a5

Please sign in to comment.