forked from dominikbraun/graph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtraversal.go
137 lines (117 loc) · 3.5 KB
/
traversal.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
package graph
import "fmt"
// DFS performs a depth-first search on the graph, starting from the given vertex. The visit
// function will be invoked with the hash of the vertex currently visited. If it returns false, DFS
// will continue traversing the graph, and if it returns true, the traversal will be stopped. In
// case the graph is disconnected, only the vertices joined with the starting vertex are visited.
//
// This example prints all vertices of the graph in DFS-order:
//
// g := graph.New(graph.IntHash)
//
// g.AddVertex(1)
// g.AddVertex(2)
// g.AddVertex(3)
//
// _ = g.AddEdge(1, 2)
// _ = g.AddEdge(2, 3)
// _ = g.AddEdge(3, 1)
//
// _ = graph.DFS(g, 1, func(value int) bool {
// fmt.Println(value)
// return false
// })
//
// Similarly, if you have a graph of City vertices and the traversal should stop at London, the
// visit function would look as follows:
//
// func(c City) bool {
// return c.Name == "London"
// }
//
// DFS is non-recursive and maintains a stack instead.
func DFS[K comparable, T any](g Graph[K, T], start K, visit func(K) bool) error {
adjacencyMap, err := g.AdjacencyMap()
if err != nil {
return fmt.Errorf("could not get adjacency map: %w", err)
}
if _, ok := adjacencyMap[start]; !ok {
return fmt.Errorf("could not find start vertex with hash %v", start)
}
stack := make([]K, 0)
visited := make(map[K]bool)
stack = append(stack, start)
for len(stack) > 0 {
currentHash := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if _, ok := visited[currentHash]; !ok {
// Stop traversing the graph if the visit function returns true.
if stop := visit(currentHash); stop {
break
}
visited[currentHash] = true
for adjacency := range adjacencyMap[currentHash] {
stack = append(stack, adjacency)
}
}
}
return nil
}
// BFS performs a depth-first search on the graph, starting from the given vertex. The visit
// function will be invoked with the hash of the vertex currently visited. If it returns false, BFS
// will continue traversing the graph, and if it returns true, the traversal will be stopped. In
// case the graph is disconnected, only the vertices joined with the starting vertex are visited.
//
// This example prints all vertices of the graph in BFS-order:
//
// g := graph.New(graph.IntHash)
//
// g.AddVertex(1)
// g.AddVertex(2)
// g.AddVertex(3)
//
// _ = g.AddEdge(1, 2)
// _ = g.AddEdge(2, 3)
// _ = g.AddEdge(3, 1)
//
// _ = graph.BFS(g, 1, func(value int) bool {
// fmt.Println(value)
// return false
// })
//
// Similarly, if you have a graph of City vertices and the traversal should stop at London, the
// visit function would look as follows:
//
// func(c City) bool {
// return c.Name == "London"
// }
//
// BFS is non-recursive and maintains a stack instead.
func BFS[K comparable, T any](g Graph[K, T], start K, visit func(K) bool) error {
adjacencyMap, err := g.AdjacencyMap()
if err != nil {
return fmt.Errorf("could not get adjacency map: %w", err)
}
if _, ok := adjacencyMap[start]; !ok {
return fmt.Errorf("could not find start vertex with hash %v", start)
}
queue := make([]K, 0)
visited := make(map[K]bool)
visited[start] = true
queue = append(queue, start)
for len(queue) > 0 {
currentHash := queue[0]
queue = queue[1:]
// Stop traversing the graph if the visit function returns true.
if stop := visit(currentHash); stop {
break
}
for adjacency := range adjacencyMap[currentHash] {
if _, ok := visited[adjacency]; !ok {
visited[adjacency] = true
queue = append(queue, adjacency)
}
}
}
return nil
}