1+ import java .util .Arrays ;
2+ import java .util .List ;
3+ import java .util .ArrayList ;
4+
5+ // A class to represent the graph and run the Bellman-Ford algorithm
6+ class BellmanFord {
7+
8+ // A nested class to represent a weighted edge in the graph
9+ static class Edge {
10+ int source , destination , weight ;
11+
12+ Edge (int source , int destination , int weight ) {
13+ this .source = source ;
14+ this .destination = destination ;
15+ this .weight = weight ;
16+ }
17+ }
18+
19+ private final int V ; // Number of vertices
20+ private final List <Edge > edges ; // List of all edges
21+
22+ public BellmanFord (int V ) {
23+ this .V = V ;
24+ this .edges = new ArrayList <>();
25+ }
26+
27+ public void addEdge (int source , int destination , int weight ) {
28+ edges .add (new Edge (source , destination , weight ));
29+ }
30+
31+ /**
32+ * Finds the shortest paths from a source vertex to all other vertices
33+ * in a weighted directed graph using the Bellman-Ford algorithm.
34+ *
35+ * ALGORITHM DESCRIPTION:
36+ * --------------------
37+ * The Bellman-Ford algorithm is a single-source shortest path algorithm that works on
38+ * directed, weighted graphs. Its key advantage over Dijkstra's algorithm is its ability
39+ * to handle graphs with negative edge weights. It can also detect if the graph
40+ * contains a negative-weight cycle that is reachable from the source.
41+ *
42+ * DYNAMIC PROGRAMMING & RELAXATION APPROACH:
43+ * The algorithm works by iteratively "relaxing" edges. A relaxation step for an edge (u, v)
44+ * with weight 'w' consists of checking if the shortest known path to 'v' can be improved
45+ * by going through 'u'.
46+ *
47+ * Core Idea:
48+ * If dist[u] + weight(u, v) < dist[v], then we update dist[v] = dist[u] + weight(u, v).
49+ *
50+ * Algorithm Steps:
51+ * 1. INITIALIZATION:
52+ * - Create a distance array `dist[]` of size V (number of vertices).
53+ * - Initialize the distance to the source vertex as 0.
54+ * - Initialize the distances to all other vertices as INFINITY.
55+ *
56+ * 2. ITERATIVE RELAXATION:
57+ * - Repeat the following loop V-1 times:
58+ * - For each edge (u, v) with weight 'w' in the graph:
59+ * - Perform the relaxation step: if `dist[u] + w < dist[v]`, update `dist[v]`.
60+ * - Why V-1 times? A simple shortest path in a graph with V vertices can have at most V-1 edges.
61+ * In the worst case, each iteration finds the next edge in the shortest path. After V-1
62+ * iterations, we guarantee that we have found all shortest paths if no negative cycle exists.
63+ *
64+ * 3. NEGATIVE CYCLE DETECTION:
65+ * - After the V-1 iterations, perform one final iteration over all edges.
66+ * - If for any edge (u, v), we can still perform a relaxation (i.e., if `dist[u] + w < dist[v]`),
67+ * it means the path can be shortened indefinitely. This is only possible if there is a
68+ * negative-weight cycle in the graph. In this case, the algorithm reports the cycle's existence.
69+ *
70+ * COMPLEXITY ANALYSIS:
71+ * --------------------
72+ * Time Complexity: O(V * E)
73+ * where V is the number of vertices and E is the number of edges.
74+ * The main logic consists of a loop that runs V-1 times (or V times, including the
75+ * cycle check), and inside this loop, we iterate over all E edges.
76+ *
77+ * Space Complexity: O(V)
78+ * This is the auxiliary space required to store the `dist` array. The space for the
79+ * graph representation itself (the edge list) is O(E). So total space is O(V + E).
80+ *
81+ * @param source The source vertex from which to find shortest paths.
82+ */
83+ public void runBellmanFord (int source ) {
84+ // Step 1: Initialize distances array.
85+ // Set distance to source as 0 and all others as infinite.
86+ int [] dist = new int [V ];
87+ Arrays .fill (dist , Integer .MAX_VALUE );
88+ dist [source ] = 0 ;
89+
90+ // Step 2: Relax all edges V-1 times.
91+ // A simple shortest path from source to any other vertex can have at most V-1 edges.
92+ for (int i = 1 ; i < V ; ++i ) {
93+ // Iterate through all edges in the graph
94+ for (Edge edge : edges ) {
95+ int u = edge .source ;
96+ int v = edge .destination ;
97+ int weight = edge .weight ;
98+
99+ // Perform the relaxation step
100+ // Check if a shorter path to 'v' is found through 'u'
101+ if (dist [u ] != Integer .MAX_VALUE && dist [u ] + weight < dist [v ]) {
102+ dist [v ] = dist [u ] + weight ;
103+ }
104+ }
105+ }
106+
107+ // Step 3: Check for negative-weight cycles.
108+ // This loop runs one more time (the V-th time) to detect cycles.
109+ for (Edge edge : edges ) {
110+ int u = edge .source ;
111+ int v = edge .destination ;
112+ int weight = edge .weight ;
113+
114+ // If we can still relax an edge, it means a negative cycle exists.
115+ if (dist [u ] != Integer .MAX_VALUE && dist [u ] + weight < dist [v ]) {
116+ System .out .println ("Graph contains a negative weight cycle!" );
117+ return ; // Exit if a cycle is detected
118+ }
119+ }
120+
121+ // If no negative cycle, print the distances
122+ printDistances (dist , source );
123+ }
124+
125+ private void printDistances (int [] dist , int source ) {
126+ System .out .println ("Shortest distances from source vertex " + source + ":" );
127+ for (int i = 0 ; i < V ; ++i ) {
128+ if (dist [i ] == Integer .MAX_VALUE ) {
129+ System .out .println ("Vertex " + i + " is unreachable." );
130+ } else {
131+ System .out .println ("Vertex " + i + " \t at distance \t " + dist [i ]);
132+ }
133+ }
134+ }
135+
136+ public static void main (String [] args ) {
137+ int V = 5 ; // 5 vertices
138+ BellmanFord graph = new BellmanFord (V );
139+
140+ // Add edges to the graph
141+ graph .addEdge (0 , 1 , -1 );
142+ graph .addEdge (0 , 2 , 4 );
143+ graph .addEdge (1 , 2 , 3 );
144+ graph .addEdge (1 , 3 , 2 );
145+ graph .addEdge (1 , 4 , 2 );
146+ graph .addEdge (3 , 2 , 5 );
147+ graph .addEdge (3 , 1 , 1 );
148+ graph .addEdge (4 , 3 , -3 );
149+
150+ int sourceVertex = 0 ;
151+ graph .runBellmanFord (sourceVertex );
152+
153+ /*
154+ Example for Negative Cycle Detection:
155+
156+ int V_cycle = 4;
157+ BellmanFord cycleGraph = new BellmanFord(V_cycle);
158+ cycleGraph.addEdge(0, 1, 1);
159+ cycleGraph.addEdge(1, 2, -1);
160+ cycleGraph.addEdge(2, 3, -1);
161+ cycleGraph.addEdge(3, 0, -1); // This edge creates a negative cycle
162+
163+ System.out.println("\n--- Testing Graph with Negative Cycle ---");
164+ cycleGraph.runBellmanFord(0);
165+ */
166+ }
167+ }
0 commit comments