sort.go

v0.2.0
Doc Versions Source
1
package graphutil
2
3
import (
4
	"fmt"
5
6
	"github.com/dominikbraun/graph"
7
8
	"go.bigb.es/auxilia/collect"
9
	"go.bigb.es/auxilia/collect/set"
10
)
11
12
// Roots returns a list of all vertices that have no incoming edges.
13
func Roots[K comparable, T any](g graph.Graph[K, T]) []K {
14
	roots := make([]K, 0)
15
16
	pMap, err := g.PredecessorMap()
17
	if err != nil {
18
		return roots
19
	}
20
21
	for vertex := range pMap {
22
		if len(pMap[vertex]) == 0 {
23
			roots = append(roots, vertex)
24
		}
25
	}
26
27
	return roots
28
}
29
30
func RecursiveDFSAfter[K comparable, T any](g graph.Graph[K, T], start K, visit func(K)) error {
31
	adjacencyMap, err := g.AdjacencyMap()
32
	if err != nil {
33
		return fmt.Errorf("could not get adjacency map: %w", err)
34
	}
35
36
	if _, ok := adjacencyMap[start]; !ok {
37
		return fmt.Errorf("could not find start vertex with hash %v", start)
38
	}
39
40
	vertices, err := g.Order()
41
	if err != nil {
42
		return fmt.Errorf("could not get number of vertices: %w", err)
43
	}
44
	nodeMark := set.NewSet[K](vertices)
45
46
	var recursiveCall func(hash K)
47
48
	recursiveCall = func(hash K) {
49
		if !nodeMark.Add(hash) {
50
			return
51
		}
52
53
		for adjacency := range adjacencyMap[hash] {
54
			recursiveCall(adjacency)
55
		}
56
57
		visit(hash)
58
	}
59
60
	recursiveCall(start)
61
62
	return nil
63
}
64
65
func ChronologicalSort[K comparable, T any](g graph.Graph[K, T]) ([]K, error) {
66
	roots := Roots(g)
67
68
	if len(roots) == 0 {
69
		return nil, fmt.Errorf("graph has no roots")
70
	}
71
72
	vertices, err := g.Order()
73
	if err != nil {
74
		return nil, fmt.Errorf("could not get number of vertices: %w", err)
75
	}
76
	order := make([]K, 0, vertices)
77
78
	for _, root := range roots {
79
		if err := RecursiveDFSAfter(g, root, func(hash K) {
80
			order = append(order, hash)
81
		}); err != nil {
82
			return nil, fmt.Errorf("could not perform DFS: %w", err)
83
		}
84
	}
85
86
	return collect.UniqueComparableOrdered(order), nil
87
}
88

Source Files