Dijkstra's Algorithm Single Source Shortest Path in DSA Python - Time & Space Complexity
We want to understand how the time needed by Dijkstra's algorithm changes as the graph size grows.
Specifically, how the number of nodes and edges affects the work done to find shortest paths.
Analyze the time complexity of the following Dijkstra's algorithm code.
import heapq
def dijkstra(graph, start):
distances = {node: float('inf') for node in graph}
distances[start] = 0
pq = [(0, start)]
while pq:
current_distance, current_node = heapq.heappop(pq)
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node].items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, neighbor))
return distances
This code finds the shortest distance from one start node to all others using a priority queue.
Look for loops and repeated steps that take most time.
- Primary operation: Removing the smallest distance node from the priority queue and checking its neighbors.
- How many times: Each edge is checked at most once when relaxing distances, and each node is popped from the queue at least once.
As the number of nodes (n) and edges (e) grow, the operations increase roughly proportional to e and n.
| Input Size (n nodes, e edges) | Approx. Operations |
|---|---|
| 10 nodes, 20 edges | About 20 edge checks and 10 queue pops |
| 100 nodes, 500 edges | About 500 edge checks and 100 queue pops |
| 1000 nodes, 5000 edges | About 5000 edge checks and 1000 queue pops |
Pattern observation: The work grows roughly with the number of edges and nodes combined.
Time Complexity: O((n + e) log n)
This means the time grows with nodes and edges, multiplied by the cost of managing the priority queue.
[X] Wrong: "Dijkstra's algorithm always runs in O(n²) time regardless of data structures."
[OK] Correct: Using a priority queue reduces the time to O((n + e) log n), which is faster especially for sparse graphs.
Understanding this time complexity helps you explain how your solution scales and why using the right data structures matters.
"What if we used a simple list instead of a priority queue? How would the time complexity change?"
Practice
Solution
Step 1: Understand the algorithm's goal
Dijkstra's Algorithm is designed to find the shortest distance from one starting point (source) to every other point in a graph.Step 2: Compare with other graph algorithms
Other algorithms like max flow or cycle detection serve different purposes, so they do not match Dijkstra's goal.Final Answer:
To find the shortest path from a single source to all other vertices -> Option DQuick Check:
Shortest path = find minimum distances [OK]
- Confusing with max flow algorithms
- Thinking it detects cycles
- Assuming it sorts vertices
Solution
Step 1: Identify the need for efficient minimum extraction
Dijkstra's Algorithm repeatedly picks the vertex with the smallest tentative distance, so an efficient way to get the minimum is needed.Step 2: Choose the right data structure
A heap or priority queue allows quick extraction of the smallest element, unlike lists or queues which are slower for this task.Final Answer:
Heap (Priority Queue) -> Option BQuick Check:
Priority queue = efficient min extraction [OK]
- Using a simple list which is slow
- Using a queue which is FIFO, not min-based
- Using a set which has no order
graph = {
0: [(1, 4), (2, 1)],
1: [(3, 1)],
2: [(1, 2), (3, 5)],
3: []
}Solution
Step 1: Initialize distances and start from vertex 0
Distances: 0 to 0 = 0, others infinity initially. Update neighbors: vertex 1 = 4, vertex 2 = 1.Step 2: Pick vertex with smallest distance (2), update neighbors
From vertex 2, update vertex 1: min(4, 1+2=3) = 3, vertex 3 = 1+5=6.Step 3: Pick vertex with smallest distance (1), update neighbors
From vertex 1, update vertex 3: min(6, 3+1=4) = 4.Step 4: Final distances
Distances array is [0, 3, 1, 4].Final Answer:
[0, 3, 1, 4] -> Option CQuick Check:
Distances updated stepwise = [0, 3, 1, 4] [OK]
- Not updating vertex 1 distance from vertex 2
- Ignoring better path to vertex 3
- Mixing up edge weights
def dijkstra(graph, source):
dist = {v: float('inf') for v in graph}
dist[source] = 0
visited = set()
while len(visited) < len(graph):
u = min((v for v in graph if v not in visited), key=lambda x: dist[x])
visited.add(u)
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
return dist
What is the main issue with it?Solution
Step 1: Analyze the selection of next vertex
The code uses min() over all unvisited vertices to find the smallest distance vertex, which is correct but inefficient.Step 2: Identify efficiency problem
Without a priority queue, each min() call takes O(V) time, making the algorithm slower but still correct logically.Step 3: Check for correctness vs efficiency
Though inefficient, the code updates distances and visited set correctly, so the main issue is lack of priority queue for efficiency.Final Answer:
The algorithm does not use a priority queue for efficiency -> Option AQuick Check:
Missing priority queue = inefficient but correct [OK]
- Thinking visited set is not updated
- Assuming distance updates are wrong
- Confusing unreachable vertex handling
Solution
Step 1: Understand Dijkstra's assumption on edge weights
Dijkstra's Algorithm assumes all edge weights are zero or positive to guarantee correctness.Step 2: Explain why negative weights break the algorithm
Negative weights can cause the algorithm to miss shorter paths found later, as it greedily picks the smallest tentative distance.Step 3: Clarify other options
The algorithm can handle cycles if no negative cycles exist, and it works on weighted graphs, not just unweighted.Final Answer:
Because Dijkstra's Algorithm assumes all edge weights are non-negative -> Option AQuick Check:
Negative weights break Dijkstra's assumptions [OK]
- Thinking it can't handle cycles at all
- Believing it only works on unweighted graphs
- Confusing negative cycles with negative edges
Ready to prove it?
Pass the test and this pattern moves unknown
