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  }