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  }