Skip to content

Commit 7985f28

Browse files
williamfisetclaude
andauthored
Refactor BFS, remove FastQueue and Recursive duplicates (williamfiset#1296)
* Refactor BFS, remove FastQueue and Recursive duplicates Remove BreadthFirstSearchAdjacencyListIterativeFastQueue (custom IntQueue variant redundant with ArrayDeque) and BreadthFirstSearchRecursive (recursive BFS with queue-passing hack). Refactor the remaining BFS implementation with improved docs, final fields, and cleaner code style. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Rename BreadthFirstSearchAdjacencyListIterative to BreadthFirstSearchAdjacencyList Drop "Iterative" from the class and file name now that the recursive and fast-queue variants have been removed. Replace the 13-node example with a simpler 8-node graph including a visual diagram. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Remove cost from Edge class and simplify helpers for unweighted BFS BFS operates on unweighted graphs, so the cost field and weighted edge helpers are unnecessary. Rename addUnweightedUndirectedEdge to addUndirectedEdge. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Simplify reconstructPath: use LinkedList.addFirst to avoid reverse Co-Authored-By: Claude Opus 4.6 <[email protected]> * Fix Integer reference comparison bug in reconstructPath Use intValue() instead of != to avoid reference comparison failure for node indices >= 128 (outside Integer cache range). Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
1 parent 5a8797a commit 7985f28

8 files changed

Lines changed: 165 additions & 438 deletions

File tree

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,7 @@ $ java -cp classes com.williamfiset.algorithms.search.BinarySearch
204204
- [Articulation points/cut vertices (adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/ArticulationPointsAdjacencyList.java) **- O(V+E)**
205205
- [Bellman-Ford (edge list, negative cycles, fast & optimized)](src/main/java/com/williamfiset/algorithms/graphtheory/BellmanFordEdgeList.java) **- O(VE)**
206206
- [:movie_camera:](https://www.youtube.com/watch?v=lyw4FaxrwHg) [Bellman-Ford (adjacency list, negative cycles)](src/main/java/com/williamfiset/algorithms/graphtheory/BellmanFordAdjacencyList.java) **- O(VE)**
207-
- [:movie_camera:](https://www.youtube.com/watch?v=oDqjPvD54Ss) [Breadth first search (adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/BreadthFirstSearchAdjacencyListIterative.java) **- O(V+E)**
208-
- [Breadth first search (adjacency list, fast queue)](src/main/java/com/williamfiset/algorithms/graphtheory/BreadthFirstSearchAdjacencyListIterativeFastQueue.java) **- O(V+E)**
207+
- [:movie_camera:](https://www.youtube.com/watch?v=oDqjPvD54Ss) [Breadth first search (adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/BreadthFirstSearchAdjacencyList.java) **- O(V+E)**
209208
- [Bridges/cut edges (adjacency list)](src/main/java/com/williamfiset/algorithms/graphtheory/BridgesAdjacencyList.java) **- O(V+E)**
210209
- [Boruvkas (adjacency list, min spanning tree algorithm)](src/main/java/com/williamfiset/algorithms/graphtheory/Boruvkas.java) **- O(Elog(V))**
211210
- [Find connected components (adjacency list, union find)](src/main/java/com/williamfiset/algorithms/graphtheory/ConnectedComponentsAdjacencyList.java) **- O(Elog(E))**

src/main/java/com/williamfiset/algorithms/graphtheory/BUILD

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,10 @@ java_binary(
5555
runtime_deps = [":graphtheory"],
5656
)
5757

58-
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:BreadthFirstSearchAdjacencyListIterative
58+
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:BreadthFirstSearchAdjacencyList
5959
java_binary(
60-
name = "BreadthFirstSearchAdjacencyListIterative",
61-
main_class = "com.williamfiset.algorithms.graphtheory.BreadthFirstSearchAdjacencyListIterative",
62-
runtime_deps = [":graphtheory"],
63-
)
64-
65-
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:BreadthFirstSearchAdjacencyListIterativeFastQueue
66-
java_binary(
67-
name = "BreadthFirstSearchAdjacencyListIterativeFastQueue",
68-
main_class = "com.williamfiset.algorithms.graphtheory.BreadthFirstSearchAdjacencyListIterativeFastQueue",
69-
runtime_deps = [":graphtheory"],
70-
)
71-
72-
# bazel run //src/main/java/com/williamfiset/algorithms/graphtheory:BreadthFirstSearchRecursive
73-
java_binary(
74-
name = "BreadthFirstSearchRecursive",
75-
main_class = "com.williamfiset.algorithms.graphtheory.BreadthFirstSearchRecursive",
60+
name = "BreadthFirstSearchAdjacencyList",
61+
main_class = "com.williamfiset.algorithms.graphtheory.BreadthFirstSearchAdjacencyList",
7662
runtime_deps = [":graphtheory"],
7763
)
7864

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* Breadth-First Search (BFS) — Adjacency List
3+
*
4+
* <p>Explores a graph level by level outward from a starting node using a
5+
* FIFO queue. Because BFS visits nodes in order of increasing distance,
6+
* it naturally finds the shortest path (fewest edges) between two nodes
7+
* in an unweighted graph.
8+
*
9+
* <p>Algorithm:
10+
* <ol>
11+
* <li>Enqueue the start node and mark it visited.</li>
12+
* <li>Dequeue a node, then enqueue all its unvisited neighbours
13+
* (marking each visited and recording the parent pointer).</li>
14+
* <li>Repeat until the queue is empty.</li>
15+
* <li>Reconstruct the shortest path by following parent pointers
16+
* from the end node back to the start.</li>
17+
* </ol>
18+
*
19+
* <p>Time: O(V + E)
20+
* <p>Space: O(V)
21+
*
22+
* @author William Fiset, [email protected]
23+
*/
24+
package com.williamfiset.algorithms.graphtheory;
25+
26+
import java.util.ArrayDeque;
27+
import java.util.ArrayList;
28+
import java.util.Deque;
29+
import java.util.LinkedList;
30+
import java.util.List;
31+
32+
public class BreadthFirstSearchAdjacencyList {
33+
34+
public static class Edge {
35+
int from, to;
36+
37+
public Edge(int from, int to) {
38+
this.from = from;
39+
this.to = to;
40+
}
41+
}
42+
43+
private final int n;
44+
private final List<List<Edge>> graph;
45+
private Integer[] prev;
46+
47+
public BreadthFirstSearchAdjacencyList(List<List<Edge>> graph) {
48+
if (graph == null) {
49+
throw new IllegalArgumentException("Graph can not be null");
50+
}
51+
this.n = graph.size();
52+
this.graph = graph;
53+
}
54+
55+
/**
56+
* Returns the shortest path (fewest edges) from {@code start} to {@code end}.
57+
*
58+
* @return node indices along the path, or an empty list if unreachable.
59+
*/
60+
public List<Integer> reconstructPath(int start, int end) {
61+
bfs(start);
62+
LinkedList<Integer> path = new LinkedList<>();
63+
for (Integer at = end; at != null; at = prev[at]) {
64+
path.addFirst(at);
65+
}
66+
if (path.isEmpty() || path.getFirst().intValue() != start) {
67+
return List.of();
68+
}
69+
return path;
70+
}
71+
72+
private void bfs(int start) {
73+
prev = new Integer[n];
74+
boolean[] visited = new boolean[n];
75+
Deque<Integer> queue = new ArrayDeque<>(n);
76+
77+
queue.offer(start);
78+
visited[start] = true;
79+
80+
while (!queue.isEmpty()) {
81+
int node = queue.poll();
82+
for (Edge edge : graph.get(node)) {
83+
if (!visited[edge.to]) {
84+
visited[edge.to] = true;
85+
prev[edge.to] = node;
86+
queue.offer(edge.to);
87+
}
88+
}
89+
}
90+
}
91+
92+
/* Graph helpers */
93+
94+
public static List<List<Edge>> createEmptyGraph(int n) {
95+
List<List<Edge>> graph = new ArrayList<>(n);
96+
for (int i = 0; i < n; i++) {
97+
graph.add(new ArrayList<>());
98+
}
99+
return graph;
100+
}
101+
102+
public static void addDirectedEdge(List<List<Edge>> graph, int u, int v) {
103+
graph.get(u).add(new Edge(u, v));
104+
}
105+
106+
public static void addUndirectedEdge(List<List<Edge>> graph, int u, int v) {
107+
addDirectedEdge(graph, u, v);
108+
addDirectedEdge(graph, v, u);
109+
}
110+
111+
// ==================== Main ====================
112+
113+
//
114+
// 0 --- 1 --- 2
115+
// | | |
116+
// 3 4 5
117+
// \ / \ /
118+
// 6 --- 7
119+
//
120+
// Shortest path from 0 to 7: [0, 1, 4, 7]
121+
//
122+
public static void main(String[] args) {
123+
int n = 8;
124+
List<List<Edge>> graph = createEmptyGraph(n);
125+
126+
addUndirectedEdge(graph, 0, 1);
127+
addUndirectedEdge(graph, 1, 2);
128+
addUndirectedEdge(graph, 0, 3);
129+
addUndirectedEdge(graph, 1, 4);
130+
addUndirectedEdge(graph, 2, 5);
131+
addUndirectedEdge(graph, 3, 6);
132+
addUndirectedEdge(graph, 4, 6);
133+
addUndirectedEdge(graph, 4, 7);
134+
addUndirectedEdge(graph, 5, 7);
135+
addUndirectedEdge(graph, 6, 7);
136+
137+
BreadthFirstSearchAdjacencyList solver = new BreadthFirstSearchAdjacencyList(graph);
138+
139+
System.out.println(solver.reconstructPath(0, 7)); // [0, 1, 4, 7]
140+
System.out.println(solver.reconstructPath(3, 5)); // [3, 6, 7, 5]
141+
}
142+
}

src/main/java/com/williamfiset/algorithms/graphtheory/BreadthFirstSearchAdjacencyListIterative.java

Lines changed: 0 additions & 144 deletions
This file was deleted.

0 commit comments

Comments
 (0)