-
Notifications
You must be signed in to change notification settings - Fork 485
/
tarjan.go
103 lines (92 loc) · 2.15 KB
/
tarjan.go
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
package dag
// StronglyConnectedComponents returns the list of strongly connected components
// of the graph using Tarjan algorithm.
func StronglyConnectedComponents(g *Graph) [][]Node {
nodes := g.Nodes()
t := tarjan{
nodes: make(map[Node]int, len(nodes)),
lowLink: make(map[Node]int, len(nodes)),
}
for _, n := range g.Nodes() {
// Calculate strong connect components for non-visited nodes
if t.nodes[n] == 0 {
t.tarjan(g, n)
}
}
return t.result
}
// tarjan represents Tarjan algorithm to find
// strongly connected component finding.
//
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
type tarjan struct {
index int
nodes map[Node]int
lowLink map[Node]int
stack []Node
result [][]Node
}
func (t *tarjan) tarjan(g *Graph, n Node) {
t.visit(n)
for succ := range g.outEdges[n] {
if t.nodes[succ] == 0 {
// Successor not visited, recurse on it
t.tarjan(g, succ)
t.lowLink[n] = min(t.lowLink[n], t.lowLink[succ])
} else if t.onStack(succ) {
// Successor is in stack and hence in the current SCC
t.lowLink[n] = min(t.lowLink[n], t.nodes[succ])
}
}
// If n is a root node, pop the stack and generate an SCC
if t.lowLink[n] == t.nodes[n] {
// Start a new strongly connected component
var scc []Node
for {
succ := t.pop()
// Add w to current strongly connected component.
scc = append(scc, succ)
if succ == n {
break
}
}
// Add current strongly connected component to result
t.result = append(t.result, scc)
}
}
// visit marks node as visited and pushes to the stack
func (t *tarjan) visit(n Node) {
t.index++
t.nodes[n] = t.index
t.lowLink[n] = t.index
t.push(n)
}
func min(a, b int) int {
if a <= b {
return a
}
return b
}
// push adds a node to the stack
func (t *tarjan) push(n Node) {
t.stack = append(t.stack, n)
}
// pop removes a node from the stack
func (t *tarjan) pop() Node {
n := len(t.stack)
if n == 0 {
return nil
}
node := t.stack[n-1]
t.stack = t.stack[:n-1]
return node
}
// onStack checks if node is in stack
func (t *tarjan) onStack(n Node) bool {
for _, e := range t.stack {
if n == e {
return true
}
}
return false
}