@@ -19,47 +19,56 @@ class TopologicalSort extends BaseGraph
1919 /**
2020 * run algorithm and return an ordered/sorted set of Vertices
2121 *
22- * the topologic sorting may be non-unique depending on your edges. this
23- * algorithm tries to keep the order of vertices as added to the graph in
24- * this case.
22+ * the topological sorting may be non-unique depending on your edges
2523 *
2624 * @return Vertices
2725 */
2826 public function getVertices ()
2927 {
30- $ tsl = array ();
28+ $ stack = array (); // visited nodes with unvisited children
3129 $ visited = array ();
30+ $ output = array ();
3231
33- // TODO: find alternative to recursive algorithm to avoid hitting recursion limit with ~100 nodes
32+ /** @var Vertex $top */
3433 // TODO: avoid having to reverse all vertices multiple times
34+ // pick a node to examine next - assume there are isolated nodes
35+ foreach (array_reverse ($ this ->graph ->getVertices ()->getVector ()) as $ top ) {
36+ $ tid = $ top ->getId ();
37+ if (!isset ($ visited [$ tid ])) { // don't examine if already found
38+ array_push ($ stack , $ top );
39+ }
3540
36- foreach (array_reverse ($ this ->graph ->getVertices ()->getVector ()) as $ vertex ) {
37- $ this ->visit ($ vertex , $ visited , $ tsl );
38- }
41+ while ($ stack ) {
42+ /** @var Vertex $current */
43+ $ node = end ($ stack );
44+ $ nid = $ node ->getId ();
3945
40- return new Vertices ( array_reverse ( $ tsl , true ));
41- }
46+ $ visited [ $ nid ] = false ; // temporary mark
47+ $ found = false ; // new children found during visit to this node
4248
43- protected function visit (Vertex $ vertex , array &$ visited , array &$ tsl )
44- {
45- $ vid = $ vertex ->getId ();
46- if (isset ($ visited [$ vid ])) {
47- if ($ visited [$ vid ] === false ) {
48- // temporary mark => not a DAG
49- throw new UnexpectedValueException ('Not a DAG ' );
50- }
51- // otherwise already marked/visisted => no need to check again
52- } else {
53- // temporary mark
54- $ visited [$ vid ] = false ;
49+ // find the next node to visit
50+ /** @var Vertex $child */
51+ foreach (array_reverse ($ node ->getVerticesEdgeTo ()->getVector ()) as $ child ) {
52+ $ cid = $ child ->getId ();
53+ if (!isset ($ visited [$ cid ])) {
54+ // found a new node - push it onto the stack
55+ array_push ($ stack , $ child );
56+ $ found = true ; // move onto the new node
57+ break ;
58+ } else if ($ visited [$ cid ] === false ) {
59+ // temporary mark => not a DAG
60+ throw new UnexpectedValueException ('Not a DAG ' );
61+ }
62+ }
5563
56- foreach (array_reverse ($ vertex ->getVerticesEdgeTo ()->getVector ()) as $ v ) {
57- $ this ->visit ($ v , $ visited , $ tsl );
64+ if (!$ found ) {
65+ array_pop ($ stack ); // no new children found - we're done with this node
66+ $ visited [$ nid ] = true ; // mark as visited
67+ array_push ($ output , $ node ); // add to results
68+ }
5869 }
59-
60- // mark as visited and include in result
61- $ visited [$ vid ] = true ;
62- $ tsl [$ vid ] = $ vertex ;
6370 }
71+
72+ return new Vertices (array_reverse ($ output , true ));
6473 }
6574}
0 commit comments