changeset 93:312700177979

modified topological sort to be non-recursive
author Scott Chacon <schacon@gmail.com>
date Sat, 09 May 2009 22:52:30 -0700
parents 6305f274fc63
children b88d6b214914
files toposort.py unit-tests/topo-test.py
diffstat 2 files changed, 106 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/toposort.py
+++ b/toposort.py
@@ -53,6 +53,64 @@
 
         return result
 
+    def strongly_connected_components_non(self, G):
+        """Returns a list of strongly connected components in G.
+
+         Uses Tarjan's algorithm with Nuutila's modifications.
+         Nonrecursive version of algorithm.
+
+         References:
+
+          R. Tarjan (1972). Depth-first search and linear graph algorithms.
+          SIAM Journal of Computing 1(2):146-160.
+
+          E. Nuutila and E. Soisalon-Soinen (1994).
+          On finding the strongly connected components in a directed graph.
+          Information Processing Letters 49(1): 9-14.
+
+         """
+        preorder={}
+        lowlink={}
+        scc_found={}
+        scc_queue = []
+        scc_list=[]
+        i=0     # Preorder counter
+        for source in G:
+            if source not in scc_found:
+                queue=[source]
+                while queue:
+                    v=queue[-1]
+                    if v not in preorder:
+                        i=i+1
+                        preorder[v]=i
+                    done=1
+                    v_nbrs=G[v]
+                    for w in v_nbrs.parents:
+                        if w not in preorder:
+                            queue.append(w)
+                            done=0
+                            break
+                    if done==1:
+                        lowlink[v]=preorder[v]
+                        for w in v_nbrs.parents:
+                            if w not in scc_found:
+                                if preorder[w]>preorder[v]:
+                                    lowlink[v]=min([lowlink[v],lowlink[w]])
+                                else:
+                                    lowlink[v]=min([lowlink[v],preorder[w]])
+                        queue.pop()
+                        if lowlink[v]==preorder[v]:
+                            scc_found[v]=True
+                            scc=(v,)
+                            while scc_queue and preorder[scc_queue[-1]]>preorder[v]:
+                                k=scc_queue.pop()
+                                scc_found[k]=True
+                                scc.append(k)
+                            scc_list.append(scc)
+                        else:
+                            scc_queue.append(v)
+        scc_list.sort(lambda x, y: cmp(len(y),len(x)))
+        return scc_list
 
     def topological_sort(self, graph):
         count = { }
@@ -76,13 +134,12 @@
 
         return result
 
-
     def robust_topological_sort(self, graph):
         """ First identify strongly connected components,
             then perform a topological sort on these components. """
 
-        components = self.strongly_connected_components(graph)
-
+        components = self.strongly_connected_components_non(graph)
+        
         node_component = { }
         for component in components:
             for node in component:
new file mode 100644
--- /dev/null
+++ b/unit-tests/topo-test.py
@@ -0,0 +1,46 @@
+import random, sys
+import unittest
+
+sys.path.append('../')
+
+import toposort
+
+class Ob:
+    def __init__(self, eyedee, parents):
+        self._id = eyedee
+        self.parents = parents
+        
+    def id(self):
+        return self._id
+
+#   f
+#  /\
+# e  \
+# |\  \
+# | g |
+# |/  |
+# c   d
+# |\ /
+# h b 
+# |/
+# a 
+
+class TestTopoSorting(unittest.TestCase):
+
+    def testsort(self):
+        data = {
+         'f' : Ob('f', ['d', 'e']),
+         'd' : Ob('d', ['b']),
+         'e' : Ob('e', ['c', 'g']),
+         'g' : Ob('g', ['c']),
+         'c' : Ob('c', ['b', 'h']),
+         'b' : Ob('b', ['a']),
+         'h' : Ob('h', ['a']),
+         'a' : Ob('a', []),
+        }
+        d = toposort.TopoSort(data).items()
+        sort = ['a', 'b', 'd', 'h', 'c', 'g', 'e', 'f']
+        self.assertEquals(d, sort)
+
+if __name__ == '__main__':
+    unittest.main()