Shortest Path Algorithms
Shortest path algorithms solve a fundamental graph problem: given a weighted graph, what's the cheapest way to get from one vertex to another? "Cheapest" here means the path whose edge weights sum to the smallest total.
Two algorithms dominate this space. Dijkstra's algorithm is faster but only works when all edge weights are non-negative. Bellman-Ford is slower but handles negative edge weights and can detect negative cycles. Knowing when to use each one is just as important as knowing how they work.
The Shortest Path Problem
In a weighted graph, each edge has a numeric weight representing some cost: distance in miles, latency in milliseconds, price in dollars, etc. The shortest path between two vertices is the path where the sum of these weights is minimized. This isn't necessarily the path with the fewest edges.
Common applications:
- Navigation systems (Google Maps, Waze): finding the fastest or shortest driving route between two locations
- Network routing: protocols like OSPF use shortest path computations to route packets efficiently across the internet
- Supply chain optimization: minimizing transportation cost or delivery time across a logistics network
- Social networks: measuring degrees of separation between users or finding connection paths for recommendations

Process of Dijkstra's Algorithm
Dijkstra's works by greedily expanding outward from the source, always processing the closest unvisited vertex next. It maintains a running estimate of the shortest distance to every vertex and progressively locks in final answers.
- Initialize distances. Set the source vertex's distance to 0. Set every other vertex's distance to .
- Insert the source into a priority queue (min-heap), keyed by distance.
- Extract the minimum. Pull the vertex with the smallest distance from the priority queue. Call it .
- Relax each neighbor. For every neighbor of , compute the tentative distance: . If this value is less than 's current distance, update 's distance and record as 's predecessor. Then insert or update in the priority queue.
- Repeat steps 3–4 until the priority queue is empty.
Once the queue is empty, you have the shortest path distances from the source to all reachable vertices. To reconstruct the actual path, trace back through the predecessor pointers.
Why does the greedy approach work here? When all weights are non-negative, the vertex with the smallest tentative distance can never be improved later. That's why Dijkstra's processes it and never revisits it. If negative weights exist, this guarantee breaks, and the algorithm can produce wrong answers.

Process of Bellman-Ford Algorithm
Bellman-Ford takes a different approach: instead of greedily picking the next vertex, it repeatedly relaxes every edge in the graph. It's slower, but this brute-force relaxation handles negative edge weights correctly.
-
Initialize distances. Set the source vertex's distance to 0 and all others to .
-
Relax all edges times. Loop iterations. In each iteration, go through every edge in the graph. If , update and set as 's predecessor.
-
Check for negative cycles. Do one more pass over all edges. If any distance can still be reduced, a negative cycle exists, meaning you can keep looping around it to decrease the total weight forever. In that case, shortest paths are undefined.
-
If no distance was updated in step 3, the algorithm has found the correct shortest paths from the source to all reachable vertices.
Why iterations? The longest possible shortest path in a graph with vertices uses at most edges. Each iteration guarantees that paths using one more edge are correctly computed. So after rounds, all shortest paths are finalized.
Complexities of Shortest Path Algorithms
|Dijkstra's|Bellman-Ford| |---|---|---| |Time complexity| with a min-heap|| | Negative edge weights | Cannot handle them | Handles them correctly | | Negative cycle detection | No | Yes | |Best used when|All edge weights are non-negative|Negative weights are possible, or you need cycle detection| Dijkstra's gets its time complexity from extracting each vertex from the heap () and updating neighbors through edge relaxations (). With a simple array instead of a heap, the complexity becomes , which can actually be better for dense graphs.
Bellman-Ford performs passes over all edges, giving . This is noticeably slower than Dijkstra's on large graphs with non-negative weights, so you should default to Dijkstra's when negative weights aren't a concern.
One limitation to keep in mind: Bellman-Ford can only detect negative cycles that are reachable from the source vertex. If a negative cycle exists in a disconnected part of the graph, the standard algorithm won't flag it.