github.com/qiniu/go-sdk/v7@v7.27.0/internal/pkg/toposort/toposort.go (about) 1 // Package toposort 提供有向无环图的拓扑排序。 2 // 3 // 本实现源自 github.com/gammazero/toposort v0.1.1,按 MIT 许可证分发,详见本目录的 LICENSE 文件。 4 package toposort 5 6 import ( 7 "errors" 8 "fmt" 9 ) 10 11 // Edge represents a pair of vertexes. Each vertex is an opaque type. 12 type Edge [2]interface{} 13 14 // Toposort performs a topological sort of the DAG defined by given edges. 15 // 16 // Takes a slice of Edge, where each element is a vertex pair representing an 17 // edge in the graph. Each pair can also be considered a dependency 18 // relationship where Edge[0] must happen before Edge[1]. For a reversed 19 // order, call ToposortR(). 20 // 21 // To include a node that is not connected to the rest of the graph, include a 22 // node with one nil vertex. It can appear anywhere in the sorted output. 23 // 24 // Returns an ordered list of vertexes where each vertex occurs before any of 25 // its destination vertexes. An error is returned if a cycle is detected. 26 func Toposort(edges []Edge) ([]interface{}, error) { 27 g, err := makeGraph(edges) 28 if err != nil { 29 return nil, err 30 } 31 sorted := make([]interface{}, 0, len(g)) 32 33 // Create map of vertexes to incoming edge count, and set counts to 0 34 inDegree := make(map[interface{}]int, len(g)) 35 for n := range g { 36 inDegree[n] = 0 37 } 38 39 // For each vertex u, get adjacent list 40 for _, adjacent := range g { 41 // For each vertex v adjacent to u 42 for _, v := range adjacent { 43 // Increment inDegree[v] 44 inDegree[v]++ 45 } 46 } 47 48 // Make a list next consisting of all vertexes u such that inDegree[u] = 0 49 var next []interface{} 50 for u, deg := range inDegree { 51 if deg == 0 { 52 next = append(next, u) 53 } 54 } 55 56 // While next is not empty... 57 for len(next) > 0 { 58 // Pop a vertex from next and call it vertex u 59 u := next[len(next)-1] 60 next = next[:len(next)-1] 61 62 // Add u to the end sorted list 63 sorted = append(sorted, u) 64 65 // For each vertex v adjacent to sorted vertex u 66 for _, v := range g[u] { 67 // Decrement count of incoming edges 68 inDegree[v]-- 69 // Enqueue nodes with no incoming edges 70 if inDegree[v] == 0 { 71 next = append(next, v) 72 } 73 } 74 } 75 76 // Check for cycle 77 if len(sorted) < len(g) { 78 var cycleNodes []string 79 for u, deg := range inDegree { 80 if deg != 0 { 81 cycleNodes = append(cycleNodes, fmt.Sprint(u)) 82 } 83 } 84 return nil, fmt.Errorf("graph contains cycle in nodes %s", cycleNodes) 85 } 86 87 // Return the sorted vertex list 88 return sorted, nil 89 } 90 91 // ToposortR is the same as Toposort with the order of the output reversed. 92 // This has the same effect as changing the vertex order of each edge. 93 func ToposortR(edges []Edge) ([]interface{}, error) { 94 sorted, err := Toposort(edges) 95 if err != nil { 96 return nil, err 97 } 98 // Reverse slice 99 for i := len(sorted)/2 - 1; i >= 0; i-- { 100 opp := len(sorted) - 1 - i 101 sorted[i], sorted[opp] = sorted[opp], sorted[i] 102 } 103 return sorted, nil 104 } 105 106 // makeGraph creates a map of source node to destination nodes. An edge with 107 // only one vertex is added to the graph, if it is not already in the graph. 108 func makeGraph(edges []Edge) (map[interface{}][]interface{}, error) { 109 graph := make(map[interface{}][]interface{}, len(edges)+1) 110 for i := range edges { 111 u, v := edges[i][0], edges[i][1] 112 if u == v { 113 return nil, errors.New("nodes in edge cannot be the same") 114 } 115 if u == nil { 116 // Add vertex only (empty destination list) 117 if _, ok := graph[v]; !ok { 118 graph[v] = nil 119 } 120 } else if v == nil { 121 if _, ok := graph[u]; !ok { 122 graph[u] = nil 123 } 124 } else { 125 graph[u] = append(graph[u], v) 126 } 127 } 128 return graph, nil 129 }