blob: b0a11a5029747a5e93f441bc21a4c30830705549 [file] [log] [blame]
Matt Jeanneretcab955f2019-04-10 15:45:57 -04001package goraph
2
3import "sync"
4
5// Tarjan finds the strongly connected components.
6// In the mathematics, a directed graph is "strongly connected"
7// if every vertex is reachable from every other node.
8// Therefore, a graph is strongly connected if there is a path
9// in each direction between each pair of node of a graph.
10// Then a pair of vertices u and v is strongly connected to each other
11// because there is a path in each direction.
12// "Strongly connected components" of an arbitrary graph
13// partition into sub-graphs that are themselves strongly connected.
14// That is, "strongly connected component" of a directed graph
15// is a sub-graph that is strongly connected.
16// Formally, "Strongly connected components" of a graph is a maximal
17// set of vertices C in G.V such that for all u, v ∈ C, there is a path
18// both from u to v, and from v to u.
19// (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
20//
21// 0. Tarjan(G):
22// 1.
23// 2. globalIndex = 0 // smallest unused index
24// 3. let S be a stack
25// 4. result = [][]
26// 5.
27// 6. for each vertex v in G:
28// 7. if v.index is undefined:
29// 8. tarjan(G, v, globalIndex, S, result)
30// 9.
31// 10. return result
32// 11.
33// 12.
34// 13. tarjan(G, v, globalIndex, S, result):
35// 14.
36// 15. v.index = globalIndex
37// 16. v.lowLink = globalIndex
38// 17. globalIndex++
39// 18. S.push(v)
40// 19.
41// 20. for each child vertex w of v:
42// 21.
43// 22. if w.index is undefined:
44// 23. recursively tarjan(G, w, globalIndex, S, result)
45// 24. v.lowLink = min(v.lowLink, w.lowLink)
46// 25.
47// 26. else if w is in S:
48// 27. v.lowLink = min(v.lowLink, w.index)
49// 28.
50// 29. // if v is the root
51// 30. if v.lowLink == v.index:
52// 31.
53// 32. // start a new strongly connected component
54// 33. component = []
55// 34.
56// 35. while True:
57// 36.
58// 37. u = S.pop()
59// 38. component.push(u)
60// 39.
61// 40. if u == v:
62// 41. result.push(component)
63// 42. break
64//
65func Tarjan(g Graph) [][]ID {
66 d := newTarjanData()
67
68 // for each vertex v in G:
69 for v := range g.GetNodes() {
70 // if v.index is undefined:
71 if _, ok := d.index[v]; !ok {
72 // tarjan(G, v, globalIndex, S, result)
73 tarjan(g, v, d)
74 }
75 }
76 return d.result
77}
78
79type tarjanData struct {
80 mu sync.Mutex // guards the following
81
82 // globalIndex is the smallest unused index
83 globalIndex int
84
85 // index is an index of a node to record
86 // the order of being discovered.
87 index map[ID]int
88
89 // lowLink is the smallest index of any index
90 // reachable from v, including v itself.
91 lowLink map[ID]int
92
93 // S is the stack.
94 S []ID
95
96 // extra map to check if a vertex is in S.
97 smap map[ID]struct{}
98
99 result [][]ID
100}
101
102func newTarjanData() *tarjanData {
103 return &tarjanData{
104 globalIndex: 0,
105 index: make(map[ID]int),
106 lowLink: make(map[ID]int),
107 S: []ID{},
108 smap: make(map[ID]struct{}),
109 result: [][]ID{},
110 }
111}
112
113func tarjan(
114 g Graph,
115 id ID,
116 data *tarjanData,
117) {
118 // This is not inherently parallelizable problem,
119 // but just to make sure.
120 data.mu.Lock()
121
122 // v.index = globalIndex
123 data.index[id] = data.globalIndex
124
125 // v.lowLink = globalIndex
126 data.lowLink[id] = data.globalIndex
127
128 // globalIndex++
129 data.globalIndex++
130
131 // S.push(v)
132 data.S = append(data.S, id)
133 data.smap[id] = struct{}{}
134
135 data.mu.Unlock()
136
137 // for each child vertex w of v:
138 cmap, err := g.GetTargets(id)
139 if err != nil {
140 panic(err)
141 }
142 for w := range cmap {
143
144 // if w.index is undefined:
145 if _, ok := data.index[w]; !ok {
146
147 // recursively tarjan(G, w, globalIndex, S, result)
148 tarjan(g, w, data)
149
150 // v.lowLink = min(v.lowLink, w.lowLink)
151 data.lowLink[id] = min(data.lowLink[id], data.lowLink[w])
152
153 } else if _, ok := data.smap[w]; ok {
154 // else if w is in S:
155
156 // v.lowLink = min(v.lowLink, w.index)
157 data.lowLink[id] = min(data.lowLink[id], data.index[w])
158 }
159 }
160
161 data.mu.Lock()
162 defer data.mu.Unlock()
163
164 // if v is the root
165 // if v.lowLink == v.index:
166 if data.lowLink[id] == data.index[id] {
167 // start a new strongly connected component
168 component := []ID{}
169
170 // while True:
171 for {
172
173 // u = S.pop()
174 u := data.S[len(data.S)-1]
175 data.S = data.S[:len(data.S)-1 : len(data.S)-1]
176 delete(data.smap, u)
177
178 // component.push(u)
179 component = append(component, u)
180
181 // if u == v:
182 if u == id {
183 data.result = append(data.result, component)
184 break
185 }
186 }
187 }
188}
189
190func min(a, b int) int {
191 if a < b {
192 return a
193 }
194 return b
195}