-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsort.go
170 lines (144 loc) · 4.45 KB
/
sort.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package go_directed_acyclic_graph
import (
"errors"
)
// Errors relating to the DFSSorter.
var (
ErrCyclicGraph = errors.New("The graph cannot be cyclic")
)
// DFSSorter topologically sorts a directed graph's nodes based on the
// directed edges between them using the Depth-first search algorithm.
type DFSSorter struct {
graph *DirectedGraph
sorted []Node
visiting map[Node]bool
discovered map[Node]bool
}
// NewDFSSorter returns a new DFS sorter.
func NewDFSSorter(graph *DirectedGraph) *DFSSorter {
return &DFSSorter{
graph: graph,
}
}
func (s *DFSSorter) init() {
s.sorted = make([]Node, 0, s.graph.NodeCount())
s.visiting = make(map[Node]bool)
s.discovered = make(map[Node]bool, s.graph.NodeCount())
}
// Sort returns the sorted nodes.
func (s *DFSSorter) Sort() ([]Node, error) {
s.init()
// > while there are unmarked nodes do
for _, node := range s.graph.Nodes() {
if err := s.visit(node); err != nil {
return nil, err
}
}
// as the nodes were appended to the slice for performance reasons,
// rather than prepended as correctly stated by the algorithm,
// we need to reverse the sorted slice
for i, j := 0, len(s.sorted)-1; i < j; i, j = i+1, j-1 {
s.sorted[i], s.sorted[j] = s.sorted[j], s.sorted[i]
}
return s.sorted, nil
}
// See https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
func (s *DFSSorter) visit(node Node) error {
// > if n has a permanent mark then return
if discovered, ok := s.discovered[node]; ok && discovered {
return nil
}
// > if n has a temporary mark then stop (not a DAG)
if visiting, ok := s.visiting[node]; ok && visiting {
return ErrCyclicGraph
}
// > mark n temporarily
s.visiting[node] = true
// > for each node m with an edge from n to m do
for _, outgoing := range s.graph.OutgoingEdges(node) {
if err := s.visit(outgoing); err != nil {
return err
}
}
s.discovered[node] = true
delete(s.visiting, node)
s.sorted = append(s.sorted, node)
return nil
}
// DFSSort returns the graph's nodes in topological order based on the
// directed edges between them using the Depth-first search algorithm.
func (g *DirectedGraph) DFSSort() ([]Node, error) {
sorter := NewDFSSorter(g)
return sorter.Sort()
}
// Errors relating to the CoffmanGrahamSorter.
var (
ErrDependencyOrder = errors.New("The topological dependency order is incorrect")
)
// CoffmanGrahamSorter sorts a graph's nodes into a sequence of levels,
// arranging so that a node which comes after another in the order is
// assigned to a lower level, and that a level never exceeds the width.
// See https://en.wikipedia.org/wiki/Coffman–Graham_algorithm
type CoffmanGrahamSorter struct {
graph *DirectedGraph
width int
}
// NewCoffmanGrahamSorter returns a new Coffman-Graham sorter.
func NewCoffmanGrahamSorter(graph *DirectedGraph, width int) *CoffmanGrahamSorter {
return &CoffmanGrahamSorter{
graph: graph,
width: width,
}
}
// Sort returns the sorted nodes.
func (s *CoffmanGrahamSorter) Sort() ([][]Node, error) {
// create a copy of the graph and remove transitive edges
reduced := s.graph.Copy()
reduced.RemoveTransitives()
// topologically sort the graph nodes
nodes, err := reduced.DFSSort()
if err != nil {
return nil, err
}
layers := make([][]Node, 0)
levels := make(map[Node]int, len(nodes))
for _, node := range nodes {
dependantLevel := -1
for _, dependant := range reduced.IncomingEdges(node) {
level, ok := levels[dependant]
if !ok {
return nil, ErrDependencyOrder
}
if level > dependantLevel {
dependantLevel = level
}
}
level := -1
// find the first unfilled layer outgoing the dependent layer
// skip this if the dependent layer is the last
if dependantLevel < len(layers)-1 {
for i := dependantLevel + 1; i < len(layers); i++ {
// ensure the layer doesn't exceed the desired width
if len(layers[i]) < s.width {
level = i
break
}
}
}
// create a new layer new none was found
if level == -1 {
layers = append(layers, make([]Node, 0, 1))
level = len(layers) - 1
}
layers[level] = append(layers[level], node)
levels[node] = level
}
return layers, nil
}
// CoffmanGrahamSort sorts the graph's nodes into a sequence of levels,
// arranging so that a node which comes after another in the order is
// assigned to a lower level, and that a level never exceeds the specified width.
func (g *DirectedGraph) CoffmanGrahamSort(width int) ([][]Node, error) {
sorter := NewCoffmanGrahamSorter(g, width)
return sorter.Sort()
}