-
Notifications
You must be signed in to change notification settings - Fork 35
/
visitor.go
157 lines (129 loc) · 3.62 KB
/
visitor.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
package dag
import (
"sort"
llq "github.com/emirpasic/gods/queues/linkedlistqueue"
lls "github.com/emirpasic/gods/stacks/linkedliststack"
)
// Visitor is the interface that wraps the basic Visit method.
// It can use the Visitor and XXXWalk functions together to traverse the entire DAG.
// And access per-vertex information when traversing.
type Visitor interface {
Visit(Vertexer)
}
// DFSWalk implements the Depth-First-Search algorithm to traverse the entire DAG.
// The algorithm starts at the root node and explores as far as possible
// along each branch before backtracking.
func (d *DAG) DFSWalk(visitor Visitor) {
d.muDAG.RLock()
defer d.muDAG.RUnlock()
stack := lls.New()
vertices := d.getRoots()
for _, id := range reversedVertexIDs(vertices) {
v := d.vertexIds[id]
sv := storableVertex{WrappedID: id, Value: v}
stack.Push(sv)
}
visited := make(map[string]bool, d.getSize())
for !stack.Empty() {
v, _ := stack.Pop()
sv := v.(storableVertex)
if !visited[sv.WrappedID] {
visited[sv.WrappedID] = true
visitor.Visit(sv)
}
vertices, _ := d.getChildren(sv.WrappedID)
for _, id := range reversedVertexIDs(vertices) {
v := d.vertexIds[id]
sv := storableVertex{WrappedID: id, Value: v}
stack.Push(sv)
}
}
}
// BFSWalk implements the Breadth-First-Search algorithm to traverse the entire DAG.
// It starts at the tree root and explores all nodes at the present depth prior
// to moving on to the nodes at the next depth level.
func (d *DAG) BFSWalk(visitor Visitor) {
d.muDAG.RLock()
defer d.muDAG.RUnlock()
queue := llq.New()
vertices := d.getRoots()
for _, id := range vertexIDs(vertices) {
v := vertices[id]
sv := storableVertex{WrappedID: id, Value: v}
queue.Enqueue(sv)
}
visited := make(map[string]bool, d.getOrder())
for !queue.Empty() {
v, _ := queue.Dequeue()
sv := v.(storableVertex)
if !visited[sv.WrappedID] {
visited[sv.WrappedID] = true
visitor.Visit(sv)
}
vertices, _ := d.getChildren(sv.WrappedID)
for _, id := range vertexIDs(vertices) {
v := vertices[id]
sv := storableVertex{WrappedID: id, Value: v}
queue.Enqueue(sv)
}
}
}
func vertexIDs(vertices map[string]interface{}) []string {
ids := make([]string, 0, len(vertices))
for id := range vertices {
ids = append(ids, id)
}
sort.Strings(ids)
return ids
}
func reversedVertexIDs(vertices map[string]interface{}) []string {
ids := vertexIDs(vertices)
i, j := 0, len(ids)-1
for i < j {
ids[i], ids[j] = ids[j], ids[i]
i++
j--
}
return ids
}
// OrderedWalk implements the Topological Sort algorithm to traverse the entire DAG.
// This means that for any edge a -> b, node a will be visited before node b.
func (d *DAG) OrderedWalk(visitor Visitor) {
d.muDAG.RLock()
defer d.muDAG.RUnlock()
queue := llq.New()
vertices := d.getRoots()
for _, id := range vertexIDs(vertices) {
v := vertices[id]
sv := storableVertex{WrappedID: id, Value: v}
queue.Enqueue(sv)
}
visited := make(map[string]bool, d.getOrder())
Main:
for !queue.Empty() {
v, _ := queue.Dequeue()
sv := v.(storableVertex)
if visited[sv.WrappedID] {
continue
}
// if the current vertex has any parent that hasn't been visited yet,
// put it back into the queue, and work on the next element
parents, _ := d.GetParents(sv.WrappedID)
for parent := range parents {
if !visited[parent] {
queue.Enqueue(sv)
continue Main
}
}
if !visited[sv.WrappedID] {
visited[sv.WrappedID] = true
visitor.Visit(sv)
}
vertices, _ := d.getChildren(sv.WrappedID)
for _, id := range vertexIDs(vertices) {
v := vertices[id]
sv := storableVertex{WrappedID: id, Value: v}
queue.Enqueue(sv)
}
}
}