Hello! Welcome back to our module on Core Graph Algorithms.
In our previous lesson, we implemented Dijkstra's algorithm, an efficient greedy approach for finding the shortest path in a graph. We concluded by highlighting its crucial limitation: it fails when a graph contains edges with negative weights. This sets the stage perfectly for today's topic.
This lesson addresses the learning outcome: Implement the Bellman-Ford algorithm and explain its use for detecting negative cycles.
The Bellman-Ford algorithm is more robust than Dijkstra's. While slower, it correctly calculates shortest paths even in the presence of negative edge weights. More importantly, it provides a mechanism to detect a particularly tricky situation: negative-weight cycles, where the concept of a "shortest path" breaks down. Understanding this algorithm is essential for making correct architectural decisions when dealing with networks that might involve costs, gains, or other scenarios modeled by negative weights.
Why Do We Need Another Shortest Path Algorithm?
As we discussed, Dijkstra's greedy strategy of finalizing the path to the "closest" node at each step can lead to incorrect results if a negative edge exists. A path that initially seems longer could eventually lead through a negative edge, creating an overall shorter route that Dijkstra's would have prematurely discarded.
The Bellman-Ford algorithm takes a more methodical, less "greedy" approach. Instead of finalizing one node at a time, it repeatedly re-evaluates the paths to all nodes.
To get a quick overview of why Bellman-Ford is necessary and how it compares to Dijkstra's, this introductory video from WilliamFiset is a great start.
Bellman Ford Algorithm | Shortest path & Negative cycles | Graph Theory
This video from WilliamFiset's series on Graph Theory explains the role of the Bellman-Ford algorithm, its time complexity, and its primary use case: handling graphs with negative edge weights.
Watch the first two minutes of the video (00:00 - 01:59). Focus on understanding the trade-off: Bellman-Ford is slower than Dijkstra's but is used when negative edges are present.
The Core Logic of Bellman-Ford
The algorithm is surprisingly simple in its structure. It works by systematically relaxing every edge in the graph over multiple passes.
Here's the process:
- Initialization:
- Create a
distancearray,dist, of size (the number of vertices). - Set the distance to the source vertex
sto 0:dist[s] = 0. - Set the distances to all other vertices to infinity:
dist[v] = ∞for allv ≠ s.
- Create a
- Repeated Relaxation:
- For from 1 to :
- For each edge
(u, v)with weightwin the graph:- Perform a relaxation: if
dist[u] + w < dist[v], then updatedist[v] = dist[u] + w.
- Perform a relaxation: if
- For each edge
- For from 1 to :
That's it. After passes, the distance array will contain the shortest path distances from the source to all other vertices, assuming there are no negative-weight cycles.
The following video provides an excellent, detailed walkthrough of this process.
The 'take U forward' channel offers a very clear, step-by-step explanation of the Bellman-Ford algorithm using a concrete example. This will help you build a strong mental model of the process.
Watch the segment from 03:42 to 14:44. Pay close attention to how the distance array is updated in each iteration as the algorithm loops through the list of all edges. Notice that the order of edges doesn't matter, but you must process all of them in each pass.
Why Iterations?
You might be wondering about the "magic number" . Why does it take exactly that many iterations?
The reasoning is quite elegant. In a graph with vertices, any simple path (a path without cycles) can have at most edges.
- After the 1st iteration, Bellman-Ford guarantees to have found all shortest paths that are at most 1 edge long.
- After the 2nd iteration, it guarantees to have found all shortest paths that are at most 2 edges long.
- ...and so on.
- Therefore, after iterations, it guarantees to have found all shortest paths that are at most edges long, which covers all possible simple shortest paths.
The same video resource provides a fantastic intuition for this using a worst-case scenario.
This segment explains the core intuition behind the V-1 iterations by demonstrating a graph structure where the shortest path is discovered one edge at a time in each iteration.
Watch the explanation from 14:44 to 21:01. This is a critical part of the lesson that moves from 'how' the algorithm works to 'why' it works, a key step in developing an architectural mindset.
Detecting Negative-Weight Cycles
This brings us to the second part of our learning outcome. What if a graph contains a cycle where the sum of edge weights is negative?

If such a cycle exists and is reachable from the source, there is no "shortest" path. You could traverse the cycle infinitely, making the path cost smaller and smaller, tending towards .
Bellman-Ford has a clever way to detect this. We've established that all simple shortest paths are found within iterations. So, what happens if we run the relaxation loop one more time—a -th iteration?
- If no distances are updated during the -th iteration, the graph is free of negative cycles, and the distances we found are correct.
- If any distance is updated during the -th iteration, it means a shorter path has been found after the theoretical maximum for simple paths. This is only possible if the path involves a negative-weight cycle.
This simple check is the standard method for detecting negative cycles.
This short segment explains how to leverage the V-th iteration to detect a negative cycle.
Watch from 21:01 to 22:55. This directly addresses the second part of our learning outcome.
Implementation and Complexity Analysis
Let's consolidate the algorithm and look at its implementation and performance.
Algorithm Pseudocode:
- Initialize
distarray:dist[source] = 0, others∞. - Relaxation Loop:
for i from 1 to V-1:
for each edge (u, v) with weight w:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w - Negative Cycle Check:
for each edge (u, v) with weight w:
if dist[u] + w < dist[v]:
// Negative cycle detected.
return "Graph contains a negative cycle" - Return
distarray.
For a practical implementation, the cp-algorithms site provides a robust C++ example, including code to not just detect, but also reconstruct the negative cycle itself. This is an excellent resource for deeper study.
Bellman-Ford - finding shortest paths with negative weights
This article from cp-algorithms.com provides a formal description and C++ implementation of Bellman-Ford, including an advanced section on how to retrieve the sequence of vertices in a negative cycle.
Read the section 'The case of a negative cycle'. Focus on the criterion for detection. The implementation part showing how to reconstruct the cycle is an advanced bonus that is good to be aware of.
Given your full-stack background with JavaScript, you might also find this implementation from GeeksforGeeks helpful to see the logic in a familiar language.
Bellman Ford Algorithm (Simple Implementation)
This GeeksforGeeks page provides the algorithm steps and code in multiple languages, including JavaScript.
Review the three main steps outlined at the beginning of the article. Then, look at the JavaScript code example to see how these steps translate into a working implementation.
Complexity Analysis
- Time Complexity:
The algorithm has two main parts: the relaxation loop and the cycle detection loop. The outer loop runs times (a factor of ), and the inner loop iterates through all edges. This gives . The final check for negative cycles also iterates through all edges, which is . The dominant term is thus . - Space Complexity:
We need space for thedistancearray and to store the graph itself (e.g., as a list of edges).
Compared to Dijkstra's , Bellman-Ford is significantly slower, especially on dense graphs. This is the trade-off for its ability to handle negative weights.
Test your understanding!
Consider the graph from the image earlier, which has a negative cycle D -> F -> E -> D. Let's say we have completed the main iterations of Bellman-Ford. Now we are in the -th (6th) iteration to check for negative cycles.
Assume the current distances are dist[D] = 10, dist[E] = 8, dist[F] = 12.
During the 6th iteration, what happens when you relax the edges of the cycle (D->F, F->E, E->D)? Which distances will be updated, demonstrating the presence of the cycle?
Show answer
Let's relax the edges one by one:
-
Relax edge (D, F) with weight 2:
- Check:
dist[D] + 2 < dist[F]=>10 + 2 < 12is FALSE.dist[F]remains 12.
- Check:
-
Relax edge (F, E) with weight -3:
- Check:
dist[F] + (-3) < dist[E]=>12 - 3 < 8is FALSE.dist[E]remains 8.
- Check:
-
Relax edge (E, D) with weight -1:
- Check:
dist[E] + (-1) < dist[D]=>8 - 1 < 10is TRUE. - Update:
dist[D]becomes 7.
- Check:
Since dist[D] was updated during this extra iteration, the algorithm has detected a negative-weight cycle. If we were to continue relaxing, this new lower cost for D would propagate through the cycle, lowering the costs for F and E in the next pass, and so on indefinitely.
Conclusion
Today you've added another fundamental graph algorithm to your toolkit. The Bellman-Ford algorithm addresses a critical gap left by Dijkstra's, equipping you to handle a wider range of graph problems.
Key Takeaways:
- Bellman-Ford solves the single-source shortest path problem for graphs that may include negative edge weights.
- Its core mechanic is to relax all edges in the graph for iterations.
- It can detect negative-weight cycles by running one additional iteration (-th) and checking if any path can still be shortened.
- Its time complexity is , making it slower than Dijkstra's. The choice between them is a classic architectural trade-off based on graph properties.
Preview of the Next Lesson:
We will shift our focus from finding paths to a different kind of graph optimization problem: finding a Minimum Spanning Tree (MST). An MST is a sub-graph that connects all vertices together with the minimum possible total edge weight, without forming any cycles. We will start by implementing Kruskal's algorithm, which uses a greedy approach and a special data structure called Union-Find to solve this problem efficiently.