Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/flow/internal/dag/tarjan.go
4095 views
1
package dag
2
3
// StronglyConnectedComponents returns the list of strongly connected components
4
// of the graph using Tarjan algorithm.
5
func StronglyConnectedComponents(g *Graph) [][]Node {
6
nodes := g.Nodes()
7
t := tarjan{
8
nodes: make(map[Node]int, len(nodes)),
9
lowLink: make(map[Node]int, len(nodes)),
10
}
11
12
for _, n := range g.Nodes() {
13
// Calculate strong connect components for non-visited nodes
14
if t.nodes[n] == 0 {
15
t.tarjan(g, n)
16
}
17
}
18
return t.result
19
}
20
21
// tarjan represents Tarjan algorithm to find
22
// strongly connected component finding.
23
//
24
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
25
type tarjan struct {
26
index int
27
nodes map[Node]int
28
lowLink map[Node]int
29
stack []Node
30
31
result [][]Node
32
}
33
34
func (t *tarjan) tarjan(g *Graph, n Node) {
35
t.visit(n)
36
for succ := range g.outEdges[n] {
37
if t.nodes[succ] == 0 {
38
// Successor not visited, recurse on it
39
t.tarjan(g, succ)
40
t.lowLink[n] = min(t.lowLink[n], t.lowLink[succ])
41
} else if t.onStack(succ) {
42
// Successor is in stack and hence in the current SCC
43
t.lowLink[n] = min(t.lowLink[n], t.nodes[succ])
44
}
45
}
46
47
// If n is a root node, pop the stack and generate an SCC
48
if t.lowLink[n] == t.nodes[n] {
49
// Start a new strongly connected component
50
var scc []Node
51
for {
52
succ := t.pop()
53
// Add w to current strongly connected component.
54
scc = append(scc, succ)
55
if succ == n {
56
break
57
}
58
}
59
// Add current strongly connected component to result
60
t.result = append(t.result, scc)
61
}
62
}
63
64
// visit marks node as visited and pushes to the stack
65
func (t *tarjan) visit(n Node) {
66
t.index++
67
t.nodes[n] = t.index
68
t.lowLink[n] = t.index
69
t.push(n)
70
}
71
72
func min(a, b int) int {
73
if a <= b {
74
return a
75
}
76
return b
77
}
78
79
// push adds a node to the stack
80
func (t *tarjan) push(n Node) {
81
t.stack = append(t.stack, n)
82
}
83
84
// pop removes a node from the stack
85
func (t *tarjan) pop() Node {
86
n := len(t.stack)
87
if n == 0 {
88
return nil
89
}
90
node := t.stack[n-1]
91
t.stack = t.stack[:n-1]
92
return node
93
}
94
95
// onStack checks if node is in stack
96
func (t *tarjan) onStack(n Node) bool {
97
for _, e := range t.stack {
98
if n == e {
99
return true
100
}
101
}
102
return false
103
}
104
105