Skip to content

Commit

Permalink
Merge pull request #25 from phyrwork/topological-sort-iterative
Browse files Browse the repository at this point in the history
Replace recursive topological sort with iterative algorithm
  • Loading branch information
clue committed Oct 10, 2018
2 parents 6b15c28 + aaca6a5 commit 9a0f93b
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
// 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 9a0f93b

Please sign in to comment.