-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathbfs_test.go
More file actions
151 lines (122 loc) · 3.36 KB
/
bfs_test.go
File metadata and controls
151 lines (122 loc) · 3.36 KB
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
package bfs
import (
"testing"
)
func TestBFS_CoreTraversal(t *testing.T) {
t.Parallel()
g := NewGraph()
// 1 -> {2,3}, 2 -> {4,5}, 3 -> {6}
g.AddEdge(1, 2)
g.AddEdge(1, 3)
g.AddEdge(2, 4)
g.AddEdge(2, 5)
g.AddEdge(3, 6)
got := BFS(g, 1)
// This test assumes deterministic neighbor iteration order (because you used AddEdge in a fixed order).
// If your graph uses map iteration, this will be nondeterministic; either:
// - store adjacency as slices, or
// - sort neighbors during traversal, or
// - change this test to validate properties instead of exact order.
want := []int{1, 2, 3, 4, 5, 6}
assertIntSliceEqual(t, want, got)
}
func TestBFS_SingleNode(t *testing.T) {
t.Parallel()
g := NewGraph()
got := BFS(g, 1)
want := []int{1}
assertIntSliceEqual(t, want, got)
}
func TestBFS_LinearGraph(t *testing.T) {
t.Parallel()
g := NewGraph()
g.AddEdge(1, 2)
g.AddEdge(2, 3)
g.AddEdge(3, 4)
got := BFS(g, 1)
want := []int{1, 2, 3, 4}
assertIntSliceEqual(t, want, got)
}
func TestBFSWithLevels_Tree(t *testing.T) {
t.Parallel()
g := NewGraph()
// Perfect-ish binary tree from 1
g.AddEdge(1, 2)
g.AddEdge(1, 3)
g.AddEdge(2, 4)
g.AddEdge(2, 5)
g.AddEdge(3, 6)
g.AddEdge(3, 7)
got := BFSWithLevels(g, 1)
want := [][]int{
{1},
{2, 3},
{4, 5, 6, 7},
}
assert2DIntSliceEqual(t, want, got)
}
func TestShortestPath_Found(t *testing.T) {
t.Parallel()
g := NewGraph()
// Undirected:
// 1-2-4
// \3/
g.AddUndirectedEdge(1, 2)
g.AddUndirectedEdge(1, 3)
g.AddUndirectedEdge(2, 4)
g.AddUndirectedEdge(3, 4)
path, dist := ShortestPath(g, 1, 4)
if dist != 2 {
t.Fatalf("ShortestPath distance: want 2, got %d (path=%v)", dist, path)
}
// Multiple valid answers: [1,2,4] OR [1,3,4].
// Validate properties instead of exact path.
if len(path) != 3 || path[0] != 1 || path[2] != 4 {
t.Fatalf("ShortestPath path shape invalid: got %v", path)
}
}
func TestShortestPath_NoPath(t *testing.T) {
t.Parallel()
g := NewGraph()
g.AddEdge(1, 2)
g.AddEdge(3, 4)
path, dist := ShortestPath(g, 1, 4)
if path != nil || dist != -1 {
t.Fatalf("ShortestPath no-path: want (nil,-1), got (path=%v, dist=%d)", path, dist)
}
}
// --- helpers ---
func assertIntSliceEqual(t *testing.T, want, got []int) {
t.Helper()
if len(want) != len(got) {
t.Fatalf("slice length mismatch: want=%d got=%d\nwant=%v\ngot=%v", len(want), len(got), want, got)
}
for i := range want {
if want[i] != got[i] {
t.Fatalf("slice[%d] mismatch: want=%d got=%d\nwant=%v\ngot=%v", i, want[i], got[i], want, got)
}
}
}
func assert2DIntSliceEqual(t *testing.T, want, got [][]int) {
t.Helper()
if len(want) != len(got) {
t.Fatalf("2D slice row mismatch: want=%d got=%d\nwant=%v\ngot=%v", len(want), len(got), want, got)
}
for r := range want {
assertIntSliceEqual(t, want[r], got[r])
}
}
// TODO (edge cases you should implement yourself):
// - Graph with cycles should terminate (mark visited on enqueue).
// - Disconnected graph: only reachable nodes returned.
// - Start node == target in ShortestPath: distance 0 and path [start].
// - Nil graph / empty adjacency: define policy (panic vs empty vs single).
// - Self-loops.
// - Very large graph (10k+ nodes) performance smoke test.
//
// Bonus TODOs (variations):
// - BFS on 2D grid (maze).
// - Multi-source BFS.
// - Bidirectional BFS.
// - BFS with early termination when target found.
// - All nodes at exact distance K.