github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/digraph/tarjan.go (about) 1 package digraph 2 3 // sccAcct is used ot pass around accounting information for 4 // the StronglyConnectedComponents algorithm 5 type sccAcct struct { 6 ExcludeSingle bool 7 NextIndex int 8 NodeIndex map[Node]int 9 Stack []Node 10 SCC [][]Node 11 } 12 13 // visit assigns an index and pushes a node onto the stack 14 func (s *sccAcct) visit(n Node) int { 15 idx := s.NextIndex 16 s.NodeIndex[n] = idx 17 s.NextIndex++ 18 s.push(n) 19 return idx 20 } 21 22 // push adds a node to the stack 23 func (s *sccAcct) push(n Node) { 24 s.Stack = append(s.Stack, n) 25 } 26 27 // pop removes a node from the stack 28 func (s *sccAcct) pop() Node { 29 n := len(s.Stack) 30 if n == 0 { 31 return nil 32 } 33 node := s.Stack[n-1] 34 s.Stack = s.Stack[:n-1] 35 return node 36 } 37 38 // inStack checks if a node is in the stack 39 func (s *sccAcct) inStack(needle Node) bool { 40 for _, n := range s.Stack { 41 if n == needle { 42 return true 43 } 44 } 45 return false 46 } 47 48 // StronglyConnectedComponents implements Tarjan's algorithm to 49 // find all the strongly connected components in a graph. This can 50 // be used to detected any cycles in a graph, as well as which nodes 51 // partipate in those cycles. excludeSingle is used to exclude strongly 52 // connected components of size one. 53 func StronglyConnectedComponents(nodes []Node, excludeSingle bool) [][]Node { 54 acct := sccAcct{ 55 ExcludeSingle: excludeSingle, 56 NextIndex: 1, 57 NodeIndex: make(map[Node]int, len(nodes)), 58 } 59 for _, node := range nodes { 60 // Recurse on any non-visited nodes 61 if acct.NodeIndex[node] == 0 { 62 stronglyConnected(&acct, node) 63 } 64 } 65 return acct.SCC 66 } 67 68 func stronglyConnected(acct *sccAcct, node Node) int { 69 // Initial node visit 70 index := acct.visit(node) 71 minIdx := index 72 73 for _, edge := range node.Edges() { 74 target := edge.Tail() 75 targetIdx := acct.NodeIndex[target] 76 77 // Recurse on successor if not yet visited 78 if targetIdx == 0 { 79 minIdx = min(minIdx, stronglyConnected(acct, target)) 80 81 } else if acct.inStack(target) { 82 // Check if the node is in the stack 83 minIdx = min(minIdx, targetIdx) 84 } 85 } 86 87 // Pop the strongly connected components off the stack if 88 // this is a root node 89 if index == minIdx { 90 var scc []Node 91 for { 92 n := acct.pop() 93 scc = append(scc, n) 94 if n == node { 95 break 96 } 97 } 98 if !(acct.ExcludeSingle && len(scc) == 1) { 99 acct.SCC = append(acct.SCC, scc) 100 } 101 } 102 103 return minIdx 104 } 105 106 func min(a, b int) int { 107 if a <= b { 108 return a 109 } 110 return b 111 }