Hello! Welcome to the first lesson in our module on "Shortest Path & MST Algorithms."
Introduction
In the previous module, we explored how to traverse graphs using BFS and DFS, focusing on connectivity and cycle detection in unweighted graphs. Now, we're taking a significant step forward by introducing weights to the edges. These weights can represent distance, time, cost, or any other metric, turning our graphs into richer models of real-world problems.
This brings us to one of the most fundamental problems in graph theory: the Single-Source Shortest Path (SSSP) problem. The goal is to find the path with the minimum total weight from a designated starting node to every other node in the graph. This has direct applications in GPS navigation (finding the fastest route), network routing (finding the most efficient data path), and many other optimization scenarios.
Today's lesson fulfills the learning outcome: Apply Dijkstra's algorithm to find single-source shortest paths and explain its limitations. We will learn how this classic greedy algorithm works, see it in action, analyze its performance, and crucially, understand the specific conditions under which it operates correctly.
1. The Core Idea of Dijkstra's Algorithm
Dijkstra's algorithm, conceived by Edsger W. Dijkstra in 1956, is a greedy algorithm. This means at each step, it makes the choice that seems best at that moment. For Dijkstra's, this means always exploring the "closest" unvisited node from the source. It systematically expands a "frontier" of visited nodes, guaranteeing that once a node is finalized, the shortest path to it has been found.
To get a high-level intuition for how this works, let's watch a short, well-animated explanation.
How Dijkstra's Algorithm Works
The video 'How Dijkstra's Algorithm Works' from the 'Spanning Tree' channel provides an excellent visual and conceptual overview. It uses the relatable analogy of finding the fastest route between towns.
Please watch from the beginning to 06:16. Focus on the two main repeating steps: 1) updating estimates to neighboring towns and 2) choosing the unexplored town with the smallest estimate to visit next.
As you saw, the algorithm maintains a set of tentative distances to all nodes and iteratively refines them. Let's formalize the key components:
-
Initialization:
- A
distancesdata structure (like an array or dictionary) stores the shortest distance found so far from the source to every other node. It's initialized with 0 for the source node and infinity () for all others. - A set of
visitednodes is kept to avoid reprocessing. - A priority queue is used to efficiently store unvisited nodes and retrieve the one with the smallest tentative distance. This is the key to the algorithm's efficiency.
- A
-
The Main Loop: The algorithm repeatedly does the following until all reachable nodes are visited:
- Select: Extract the unvisited node
uwith the smallest distance from the priority queue. - Visit: Mark
uas visited. Its current distance is now considered final. - Relax Neighbors: For each neighbor
vofu, perform a "relaxation" step.
- Select: Extract the unvisited node
The Relaxation Step
Relaxation is the heart of the algorithm. It's the process of checking if the path to a neighbor v through the current node u is shorter than any path to v found so far.
The logic is simple:
If distances[u] + weight(u, v) < distances[v], then we have found a better path to v. We update distances[v] to this new, lower value.
.png)
2. A Step-by-Step Walkthrough
Now let's trace the algorithm on a more complex example to solidify your understanding of the selection and relaxation process.
3.6 Dijkstra Algorithm - Single Source Shortest Path - Greedy Method
The video 'Dijkstra Algorithm - Single Source Shortest Path' by Abdul Bari provides a clear, methodical trace of the algorithm. He demonstrates how the distances are updated step-by-step.
Watch from 01:21 to 07:42. The first part (until 04:21) explains the relaxation formula. The second part (from 04:21) applies the full algorithm to an example graph. Try to follow along and predict which node will be chosen next and which distances will be updated.
By following this process, Dijkstra's algorithm systematically finds the shortest path from the source to all other nodes. The greedy choice of always picking the closest unvisited node works because all edge weights are non-negative. This ensures that once we select a node, we won't find a "detour" through some other unvisited node that could have resulted in a shorter path.
3. Implementation and Complexity
Given your software engineering background, seeing the algorithm in code can be very illuminating. A standard implementation uses an adjacency list to represent the graph and a min-priority queue to manage the distances.
Understanding and Implementing Dijkstra's Algorithm
The article 'Understanding and Implementing Dijkstra's Algorithm' from AlgoCademy provides a clean Python implementation and a solid analysis of its complexity.
Please read sections 3 ('Implementing Dijkstra’s Algorithm') and 4 ('Time Complexity Analysis'). Pay attention to how Python's heapq module is used as a min-priority queue. The complexity analysis explains the difference between a naive implementation and an efficient one.
Complexity Analysis Summary
As the article explained, the choice of data structure for finding the minimum distance node is critical:
-
Using a simple array/list:
- In each of the iterations, we must scan all vertices to find the unvisited one with the minimum distance.
- This leads to a time complexity of . This is acceptable for dense graphs (where is close to ), but inefficient for sparse graphs (where is closer to ).
-
Using a Binary Heap (Priority Queue):
- Building the initial heap takes .
- The main loop runs times, and each
extract-minoperation from the heap takes time. - Every edge is relaxed at most once. Each relaxation might lead to a
decrease-keyoperation (by pushing a new, smaller-distance entry), which takes time. In the worst case, this happens for all edges. - This gives a total time complexity of , often simplified to or just for connected graphs. This is the standard and much more efficient approach for sparse graphs.
The space complexity is to store the graph, distances, and the priority queue.
4. The Critical Limitation: Negative Edge Weights
Dijkstra's greedy strategy hinges on one crucial assumption: all edge weights are non-negative. If a graph contains even one negative edge, the algorithm can produce incorrect results.
The reason is that the algorithm's guarantee is broken. When Dijkstra's finalizes the distance to a node u, it assumes there's no shorter path to be found. However, a path through a later-visited node v that involves a negative edge might reveal a "shortcut" back to u or one of its neighbors, a possibility the algorithm doesn't account for.

This next video segment demonstrates this failure case very clearly.
3.6 Dijkstra Algorithm - Single Source Shortest Path - Greedy Method
Let's return to Abdul Bari's video. He provides a brilliant walkthrough showing exactly where the logic of Dijkstra's breaks down when a negative edge is introduced.
Please watch from 13:50 to 18:05. Observe how the algorithm greedily commits to a path, and later, the negative edge reveals that this commitment was a mistake. This is the core reason for the limitation.
This limitation is not a minor detail; it is the single most important constraint to remember when deciding whether to use Dijkstra's algorithm.
Conclusion
In this lesson, we have taken a deep dive into one of the cornerstones of graph algorithms. You can now apply Dijkstra's algorithm and, just as importantly, know when not to apply it.
Key Takeaways:
- Dijkstra's algorithm solves the single-source shortest path problem for graphs with non-negative edge weights.
- It is a greedy algorithm that works by maintaining a set of tentative distances and always visiting the closest unvisited node.
- The core operation is relaxation, where paths to neighbors are updated if a shorter route is found.
- An efficient implementation uses a priority queue (min-heap) to achieve a time complexity of .
- The primary limitation of Dijkstra's algorithm is its failure to produce correct results on graphs containing negative edge weights.
Preview of the Next Lesson:
So, what do we do when our graph does have negative edge weights, which can happen in many real-world models (e.g., representing financial gains and losses)? In our next lesson, we will tackle this exact problem by learning the Bellman-Ford algorithm. It is slower than Dijkstra's, but it is more robust, capable of handling negative weights, and can even be used to detect the presence of "negative cycles."