Skip to content

Commit 8a5342d

Browse files
Merge pull request #430 from RashmitTopG/BellmanFord
Added Bellman Ford Algorithm
2 parents b88b7aa + b9c2945 commit 8a5342d

1 file changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Bellman–Ford — Single-Source Shortest Paths (+ Negative-Cycle Check)
3+
* ===================================================================
4+
* ✍️ Algorithm Description
5+
* -----------------------
6+
* Bellman–Ford computes shortest path distances from a single source in a
7+
* weighted directed graph — even when there are negative edge weights.
8+
* It can also detect the presence of any negative-weight cycle reachable
9+
* from the source (or anywhere, using a super-source trick).
10+
*
11+
* Core idea:
12+
* 1) Initialize dist[src] = 0, all others = +∞.
13+
* 2) Relax all edges V-1 times (where V is #vertices).
14+
* - A "relax" on edge (u -> v, w) does: dist[v] = min(dist[v], dist[u] + w).
15+
* 3) Do one more pass over all edges:
16+
* - If any distance can still be improved, a negative-weight cycle exists.
17+
*
18+
* Use cases:
19+
* - Shortest paths with negative edges (no negative cycles reachable from src)
20+
* - Financial arbitrage detection (negative cycles)
21+
* - As a subroutine in Johnson’s algorithm (reweighting for all-pairs)
22+
*
23+
* 📊 Complexity Analysis
24+
* ----------------------
25+
* Let V = number of vertices, E = number of edges.
26+
* Time: O(V * E) — V-1 relaxation rounds plus a final scan.
27+
* Space: O(V) — distance array (+ optional parent array).
28+
*
29+
* 💡 Notes
30+
* - `bellmanFord1` (array distances) is fastest when vertices are 0..V-1.
31+
* - `bellmanFordMap` (HashMap distances) is handy if vertex ids are sparse or labeled.
32+
* - `hasNegativeCycleAnywhere` adds a super-source → detects ANY neg cycle in the graph.
33+
* - Distances use `long` to reduce overflow risk. Use `INF` as a large sentinel.
34+
*/
35+
36+
import java.util.*;
37+
38+
class Solution {
39+
40+
// Use a large INF sentinel to avoid overflow when adding weights.
41+
private static final long INF = (long)1e18;
42+
43+
/**
44+
* Bellman–Ford with array distances (vertices assumed 0..V-1).
45+
*
46+
* @param V number of vertices
47+
* @param edges directed weighted edges as [u, v, w]
48+
* @param src source vertex
49+
* @return distances array; if a negative cycle is reachable from src,
50+
* returns null to signal "no well-defined shortest paths"
51+
*/
52+
public static long[] bellmanFord1(int V, int[][] edges, int src) {
53+
long[] dist = new long[V];
54+
Arrays.fill(dist, INF);
55+
dist[src] = 0;
56+
57+
// Relax edges V-1 times
58+
for (int i = 1; i <= V - 1; i++) {
59+
boolean changed = false;
60+
for (int[] e : edges) {
61+
int u = e[0], v = e[1];
62+
long w = e[2];
63+
if (dist[u] != INF && dist[u] + w < dist[v]) {
64+
dist[v] = dist[u] + w;
65+
changed = true;
66+
}
67+
}
68+
// Early exit if no update in a round
69+
if (!changed) break;
70+
}
71+
72+
// Check for a negative cycle reachable from src
73+
for (int[] e : edges) {
74+
int u = e[0], v = e[1];
75+
long w = e[2];
76+
if (dist[u] != INF && dist[u] + w < dist[v]) {
77+
return null; // Negative cycle detected (reachable from src)
78+
}
79+
}
80+
return dist;
81+
}
82+
83+
/**
84+
* Bellman–Ford with HashMap distances (good for sparse / labeled vertices).
85+
* Vertices are still 0..V-1 here, but you can adapt to arbitrary ids
86+
* by mapping them to [0..n-1] and keeping only present keys in the map.
87+
*
88+
* @param V number of vertices
89+
* @param edges directed weighted edges as [u, v, w]
90+
* @param src source vertex
91+
* @return distance map; null if a negative cycle is reachable from src
92+
*/
93+
public static HashMap<Integer, Long> bellmanFordMap(int V, int[][] edges, int src) {
94+
HashMap<Integer, Long> dist = new HashMap<>();
95+
for (int i = 0; i < V; i++) dist.put(i, INF);
96+
dist.put(src, 0L);
97+
98+
for (int i = 1; i <= V - 1; i++) {
99+
boolean changed = false;
100+
for (int[] e : edges) {
101+
int u = e[0], v = e[1];
102+
long w = e[2];
103+
long du = dist.get(u);
104+
long dv = dist.get(v);
105+
if (du != INF && du + w < dv) {
106+
dist.put(v, du + w);
107+
changed = true;
108+
}
109+
}
110+
if (!changed) break;
111+
}
112+
113+
for (int[] e : edges) {
114+
int u = e[0], v = e[1];
115+
long w = e[2];
116+
long du = dist.get(u);
117+
long dv = dist.get(v);
118+
if (du != INF && du + w < dv) {
119+
return null; // Negative cycle detected (reachable from src)
120+
}
121+
}
122+
return dist;
123+
}
124+
125+
/**
126+
* Detect ANY negative-weight cycle in the graph (not just reachable from a given src).
127+
* Technique: add a super-source S connected to all nodes with 0-weight edges, run BF.
128+
*
129+
* @param V number of vertices
130+
* @param edges directed weighted edges as [u, v, w]
131+
* @return true if any negative-weight cycle exists; false otherwise
132+
*/
133+
public static boolean hasNegativeCycleAnywhere(int V, int[][] edges) {
134+
// Build a new edge list with an extra super-source (-1) conceptually.
135+
// Implementation trick: initialize all dist[i]=0 and run V rounds.
136+
long[] dist = new long[V];
137+
Arrays.fill(dist, 0L); // Equivalent to relaxing from a super-source with 0 edges to all
138+
139+
// Relax V times; if we can still relax on the V-th time, a neg cycle exists anywhere.
140+
for (int i = 1; i <= V; i++) {
141+
boolean changed = false;
142+
for (int[] e : edges) {
143+
int u = e[0], v = e[1];
144+
long w = e[2];
145+
if (dist[u] + w < dist[v]) {
146+
dist[v] = dist[u] + w;
147+
changed = true;
148+
if (i == V) return true; // improvement on the V-th round => negative cycle
149+
}
150+
}
151+
if (!changed) break;
152+
}
153+
return false;
154+
}
155+
156+
// ----------------------------
157+
// ✅ Test Cases / Examples
158+
// ----------------------------
159+
public static void main(String[] args) {
160+
// 1) Positive weights only
161+
int V1 = 5;
162+
int[][] E1 = {
163+
{0,1,6}, {0,3,7},
164+
{1,2,5}, {1,3,8}, {1,4,-4},
165+
{2,1,-2}, {3,2,-3}, {3,4,9},
166+
{4,0,2}, {4,2,7}
167+
};
168+
System.out.println("Case 1: Positive & some negative edges (no neg cycle, src=0)");
169+
System.out.println("hasNegativeCycleAnywhere: " + hasNegativeCycleAnywhere(V1, E1)); // false
170+
System.out.println("bellmanFord1 distances from 0: " + Arrays.toString(bellmanFord1(V1, E1, 0)));
171+
172+
// 2) Negative edge but no negative cycle
173+
int V2 = 4;
174+
int[][] E2 = {
175+
{0,1,1}, {1,2,-1}, {2,3,-1}
176+
};
177+
System.out.println("\nCase 2: Negative edges, no cycle (src=0)");
178+
System.out.println("hasNegativeCycleAnywhere: " + hasNegativeCycleAnywhere(V2, E2)); // false
179+
System.out.println("bellmanFordMap distances from 0: " + bellmanFordMap(V2, E2, 0));
180+
181+
// 3) Graph with a negative cycle reachable from src
182+
// Cycle: 1 -> 2 -> 3 -> 1 with total weight -3
183+
int V3 = 4;
184+
int[][] E3 = {
185+
{0,1,2}, {1,2,-2}, {2,3,-2}, {3,1,1}
186+
};
187+
System.out.println("\nCase 3: Negative cycle reachable from src=0");
188+
System.out.println("hasNegativeCycleAnywhere: " + hasNegativeCycleAnywhere(V3, E3)); // true
189+
long[] d3 = bellmanFord1(V3, E3, 0);
190+
System.out.println("bellmanFord1 distances from 0: " + (d3 == null ? "NEGATIVE CYCLE" : Arrays.toString(d3)));
191+
192+
// 4) Disconnected graph (unreachable nodes remain INF)
193+
int V4 = 6;
194+
int[][] E4 = {
195+
{0,1,3}, {1,2,4}, // component 1
196+
{3,4,1}, {4,5,1} // component 2 (disconnected from src=0)
197+
};
198+
System.out.println("\nCase 4: Disconnected graph (src=0)");
199+
System.out.println("hasNegativeCycleAnywhere: " + hasNegativeCycleAnywhere(V4, E4)); // false
200+
long[] d4 = bellmanFord1(V4, E4, 0);
201+
System.out.println("bellmanFord1 distances from 0: " + Arrays.toString(d4));
202+
// Expect: [0, 3, 7, INF, INF, INF]
203+
204+
// 5) Single vertex, no edges
205+
int V5 = 1;
206+
int[][] E5 = {};
207+
System.out.println("\nCase 5: Single node (src=0)");
208+
System.out.println("hasNegativeCycleAnywhere: " + hasNegativeCycleAnywhere(V5, E5)); // false
209+
System.out.println("bellmanFord1 distances from 0: " + Arrays.toString(bellmanFord1(V5, E5, 0)));
210+
}
211+
}
212+

0 commit comments

Comments
 (0)