@@ -19,47 +19,56 @@ class TopologicalSort extends BaseGraph
19
19
/**
20
20
* run algorithm and return an ordered/sorted set of Vertices
21
21
*
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
25
23
*
26
24
* @return Vertices
27
25
*/
28
26
public function getVertices ()
29
27
{
30
- $ tsl = array ();
28
+ $ stack = array (); // visited nodes with unvisited children
31
29
$ visited = array ();
30
+ $ output = array ();
32
31
33
- // TODO: find alternative to recursive algorithm to avoid hitting recursion limit with ~100 nodes
32
+ /** @var Vertex $top */
34
33
// 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
+ }
35
40
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 ();
39
45
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
42
48
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
+ }
55
63
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
+ }
58
69
}
59
-
60
- // mark as visited and include in result
61
- $ visited [$ vid ] = true ;
62
- $ tsl [$ vid ] = $ vertex ;
63
70
}
71
+
72
+ return new Vertices (array_reverse ($ output , true ));
64
73
}
65
74
}
0 commit comments