github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/easy/yamlx/dag.go (about) 1 package yamlx 2 3 type dag struct { 4 nodes map[int]bool 5 edges map[int]map[int]bool 6 reverseEdges map[int]map[int]bool 7 } 8 9 func (d *dag) addVertex(n int) { 10 if d.nodes == nil { 11 d.nodes = make(map[int]bool) 12 } 13 d.nodes[n] = true 14 } 15 16 func (d *dag) addEdge(from, to int) (isCyclic bool) { 17 if d.isCyclic(from, to) { 18 return true 19 } 20 if d.nodes == nil { 21 d.nodes = make(map[int]bool) 22 } 23 d.nodes[from] = true 24 d.nodes[to] = true 25 d.edges = d._addToEdges(d.edges, from, to) 26 d.reverseEdges = d._addToEdges(d.reverseEdges, to, from) 27 return false 28 } 29 30 func (d *dag) isCyclic(from, to int) bool { 31 stack := []int{from} 32 pop := func() (n int) { 33 n = stack[len(stack)-1] 34 stack = stack[:len(stack)-1] 35 return 36 } 37 38 seen := make(map[int]bool) 39 for len(stack) > 0 { 40 m := d.reverseEdges[pop()] 41 if m[to] { 42 return true 43 } 44 for n := range m { 45 if !seen[n] { 46 stack = append(stack, n) 47 seen[n] = true 48 } 49 } 50 } 51 return false 52 } 53 54 func (d *dag) _addToEdges(edges map[int]map[int]bool, from, to int) map[int]map[int]bool { 55 if edges == nil { 56 edges = make(map[int]map[int]bool) 57 } 58 m, ok := edges[from] 59 if !ok { 60 m = make(map[int]bool) 61 edges[from] = m 62 } 63 m[to] = true 64 return edges 65 } 66 67 func (d *dag) visitVertex(f func(n int)) { 68 for n := range d.nodes { 69 f(n) 70 } 71 } 72 73 func (d *dag) visitNeighbors(from int, f func(to int)) { 74 for n := range d.edges[from] { 75 f(n) 76 } 77 } 78 79 // Kahn's algorithm 80 func (d *dag) topoSort() []int { 81 indegree := make(map[int]int) 82 d.visitVertex(func(n int) { 83 d.visitNeighbors(n, func(to int) { 84 indegree[to]++ 85 }) 86 }) 87 88 // queue holds all vertices with indegree 0. 89 var queue []int 90 d.visitVertex(func(n int) { 91 if indegree[n] == 0 { 92 queue = append(queue, n) 93 } 94 }) 95 pop := func() (n int) { 96 n = queue[0] 97 queue = queue[1:] 98 return 99 } 100 101 order := make([]int, 0, len(d.nodes)) 102 count := 0 103 for len(queue) > 0 { 104 n := pop() 105 order = append(order, n) 106 count++ 107 d.visitNeighbors(n, func(to int) { 108 indegree[to]-- 109 if indegree[to] == 0 { 110 queue = append(queue, to) 111 } 112 }) 113 } 114 115 if count != len(d.nodes) { // unreachable 116 panic("yamlx: dag is in invalid state") 117 } 118 119 return order 120 }