-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtarjan.py
157 lines (136 loc) · 4.38 KB
/
tarjan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# This code is copied from https://github.com/bwesterb/py-tarjan
from collections import namedtuple
__all__ = ['tarjan',
'tarjan_iter',
'tarjan_recursive']
""" Context used to implement the algorithm without recursion in @tarjan
and @tarjan_iter """
TarjanContext = namedtuple('TarjanContext',
['g', # the graph
'S', # The main stack of the alg.
'S_set', # == set(S) for performance
'index', # { v : <index of v> }
'lowlink', # { v : <lowlink of v> }
'T', # stack to replace recursion
'ret']) # return code
def _tarjan_head(ctx, v):
""" Used by @tarjan and @tarjan_iter. This is the head of the
main iteration """
ctx.index[v] = len(ctx.index)
ctx.lowlink[v] = ctx.index[v]
ctx.S.append(v)
ctx.S_set.add(v)
it = iter(ctx.g.get(v, ()))
ctx.T.append((it, False, v, None))
def _tarjan_body(ctx, it, v):
""" Used by @tarjan and @tarjan_iter. This is the body of the
main iteration """
for w in it:
if w not in ctx.index:
ctx.T.append((it, True, v, w))
_tarjan_head(ctx, w)
return
if w in ctx.S_set:
ctx.lowlink[v] = min(ctx.lowlink[v], ctx.index[w])
if ctx.lowlink[v] == ctx.index[v]:
scc = []
w = None
while v != w:
w = ctx.S.pop()
scc.append(w)
ctx.S_set.remove(w)
ctx.ret.append(scc)
def tarjan_iter(g):
""" Returns the strongly connected components of the graph @g
in a topological order.
@g is the graph represented as a dictionary
{ <vertex> : <successors of vertex> }.
This function does not recurse. It returns an iterator. """
ctx = TarjanContext(
g=g,
S=[],
S_set=set(),
index={},
lowlink={},
T=[],
ret=[])
main_iter = iter(g)
while True:
try:
v = next(main_iter)
except StopIteration:
return
if v not in ctx.index:
_tarjan_head(ctx, v)
while ctx.T:
it, inside, v, w = ctx.T.pop()
if inside:
ctx.lowlink[v] = min(ctx.lowlink[w],
ctx.lowlink[v])
_tarjan_body(ctx, it, v)
if ctx.ret:
assert len(ctx.ret) == 1
yield ctx.ret.pop()
def tarjan(g):
""" Returns the strongly connected components of the graph @g
in a topological order.
@g is the graph represented as a dictionary
{ <vertex> : <successors of vertex> }.
This function does not recurse. """
ctx = TarjanContext(
g=g,
S=[],
S_set=set(),
index={},
lowlink={},
T=[],
ret=[])
main_iter = iter(g)
while True:
try:
v = next(main_iter)
except StopIteration:
return ctx.ret
if v not in ctx.index:
_tarjan_head(ctx, v)
while ctx.T:
it, inside, v, w = ctx.T.pop()
if inside:
ctx.lowlink[v] = min(ctx.lowlink[w],
ctx.lowlink[v])
_tarjan_body(ctx, it, v)
def tarjan_recursive(g):
""" Returns the strongly connected components of the graph @g
in a topological order.
@g is the graph represented as a dictionary
{ <vertex> : <successors of vertex> }.
This function recurses --- large graphs may cause a stack
overflow. """
S = []
S_set = set()
index = {}
lowlink = {}
ret = []
def visit(v):
index[v] = len(index)
lowlink[v] = index[v]
S.append(v)
S_set.add(v)
for w in g.get(v, ()):
if w not in index:
visit(w)
lowlink[v] = min(lowlink[w], lowlink[v])
elif w in S_set:
lowlink[v] = min(lowlink[v], index[w])
if lowlink[v] == index[v]:
scc = []
w = None
while v != w:
w = S.pop()
scc.append(w)
S_set.remove(w)
ret.append(scc)
for v in g:
if not v in index:
visit(v)
return ret