Skip to content
Merged

Feat #6561

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions src/main/java/com/thealgorithms/graph/ZeroOneBfs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.thealgorithms.graph;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;

/**
* 0-1 BFS for shortest paths on graphs with edges weighted 0 or 1.
*
* <p>Time Complexity: O(V + E). Space Complexity: O(V).
*
* <p>References:
* <ul>
* <li>https://cp-algorithms.com/graph/01_bfs.html</li>
* </ul>
*/
public final class ZeroOneBfs {

private ZeroOneBfs() {
// Utility class; do not instantiate.
}

/**
* Computes shortest distances from {@code src} in a graph whose edges have weight 0 or 1.
*
* @param n the number of vertices, labeled {@code 0..n-1}
* @param adj adjacency list; for each vertex u, {@code adj.get(u)} is a list of pairs
* {@code (v, w)} where {@code v} is a neighbor and {@code w} is 0 or 1
* @param src the source vertex
* @return an array of distances; {@code Integer.MAX_VALUE} denotes unreachable
* @throws IllegalArgumentException if {@code n < 0}, {@code src} is out of range,
* or any edge has weight other than 0 or 1
*/
public static int[] shortestPaths(int n, List<List<int[]>> adj, int src) {
if (n < 0 || src < 0 || src >= n) {
throw new IllegalArgumentException("Invalid n or src");
}
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
Deque<Integer> dq = new ArrayDeque<>();

dist[src] = 0;
dq.addFirst(src);

while (!dq.isEmpty()) {
int u = dq.pollFirst();
List<int[]> edges = adj.get(u);
if (edges == null) {
continue;
}
for (int[] e : edges) {
if (e == null || e.length < 2) {
continue;
}
int v = e[0];
int w = e[1];
if (v < 0 || v >= n) {
continue; // ignore bad edges
}
if (w != 0 && w != 1) {
throw new IllegalArgumentException("Edge weight must be 0 or 1");
}
int nd = dist[u] + w;
if (nd < dist[v]) {
dist[v] = nd;
if (w == 0) {
dq.addFirst(v);
} else {
dq.addLast(v);
}
}
}
}
return dist;
}
}
68 changes: 68 additions & 0 deletions src/test/java/com/thealgorithms/graph/ZeroOneBfsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.thealgorithms.graph;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;

class ZeroOneBfsTest {

// Helper to build adjacency list with capacity n
private static List<List<int[]>> makeAdj(int n) {
List<List<int[]>> adj = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
adj.add(new ArrayList<>());
}
return adj;
}

@Test
void simpleLineGraph() {
int n = 4;
List<List<int[]>> adj = makeAdj(n);
// 0 --0--> 1 --1--> 2 --0--> 3
adj.get(0).add(new int[] {1, 0});
adj.get(1).add(new int[] {2, 1});
adj.get(2).add(new int[] {3, 0});

int[] dist = ZeroOneBfs.shortestPaths(n, adj, 0);
assertArrayEquals(new int[] {0, 0, 1, 1}, dist);
}

@Test
void parallelEdgesPreferZero() {
int n = 3;
List<List<int[]>> adj = makeAdj(n);
// Two edges 0->1: weight 1 and weight 0. Algorithm should choose 0.
adj.get(0).add(new int[] {1, 1});
adj.get(0).add(new int[] {1, 0});
adj.get(1).add(new int[] {2, 1});

int[] dist = ZeroOneBfs.shortestPaths(n, adj, 0);
assertArrayEquals(new int[] {0, 0, 1}, dist);
}

@Test
void unreachableNodes() {
int n = 3;
List<List<int[]>> adj = makeAdj(n);
adj.get(0).add(new int[] {1, 0});
int[] dist = ZeroOneBfs.shortestPaths(n, adj, 0);
// node 2 unreachable -> Integer.MAX_VALUE
assertArrayEquals(new int[] {0, 0, Integer.MAX_VALUE}, dist);
}

@Test
void invalidArgs() {
int n = 2;
List<List<int[]>> adj = makeAdj(n);
// invalid weight
adj.get(0).add(new int[] {1, 2});
assertThrows(IllegalArgumentException.class, () -> ZeroOneBfs.shortestPaths(n, adj, 0));
// invalid src
assertThrows(IllegalArgumentException.class, () -> ZeroOneBfs.shortestPaths(n, adj, -1));
assertThrows(IllegalArgumentException.class, () -> ZeroOneBfs.shortestPaths(n, adj, 2));
}
}