Dijkstra's Algorithm
Time Complexity
Min Heap
Algorithm Optimization
Graph Theory

Time complexity for Dijkstra's algorithm with min heap and optimizations

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Overview of Dijkstra's Algorithm

Dijkstra's algorithm is a classic method for finding the shortest path between nodes in a weighted graph with non-negative edge weights. It is widely used in networking, GPS navigation, and pathfinding in games. This article analyzes the time complexity of Dijkstra's algorithm when implemented with a min-heap and discusses common optimizations.

How Dijkstra's Algorithm Works

The algorithm starts at a chosen source node and explores neighboring nodes in order of increasing distance. It maintains a set of visited nodes and a priority queue of candidate nodes. At each step, it extracts the node with the smallest tentative distance, marks it as visited, and relaxes all its outgoing edges. "Relaxing" an edge means checking whether the path through the current node offers a shorter route to a neighbor, and updating the neighbor's distance if so.

Time Complexity Analysis with Min-Heap

The time complexity depends on the data structures used. When implemented with a binary min-heap and an adjacency list, here is the breakdown:

Initialization

Adding the source node to the priority queue and setting all distances to infinity takes O(V)O(V) time, where VV is the number of vertices.

Extract-Min Operations

Each vertex is extracted from the priority queue exactly once. Each extraction from a binary min-heap costs O(logV)O(\log V). Since there are VV extractions total, this contributes O(VlogV)O(V \log V).

Edge Relaxation (Decrease-Key Operations)

For each vertex extracted, we examine all its outgoing edges. Across the entire algorithm, every edge is examined exactly once (in a directed graph) or twice (in an undirected graph). When we find a shorter path, we perform a decrease-key operation on the priority queue, which costs O(logV)O(\log V) for a binary min-heap.

In the worst case, every edge triggers a decrease-key, so this contributes O(ElogV)O(E \log V), where EE is the number of edges.

Overall Complexity

Combining both components:

T(V,E)=O(VlogV)+O(ElogV)=O((V+E)logV)T(V, E) = O(V \log V) + O(E \log V) = O((V + E) \log V)

Since most practical graphs have EVE \geq V (a connected graph has at least V1V - 1 edges), this simplifies to O(ElogV)O(E \log V) in practice.

Comparison of Priority Queue Implementations

Different priority queue choices lead to different complexities:

Priority Queue TypeExtract-MinDecrease-KeyOverall Dijkstra
Unsorted arrayO(V)O(V)O(1)O(1)O(V2)O(V^2)
Binary min-heapO(logV)O(\log V)O(logV)O(\log V)O((V+E)logV)O((V + E) \log V)
Fibonacci heapO(logV)O(\log V) amortizedO(1)O(1) amortizedO(VlogV+E)O(V \log V + E)

When to Use Which

  • Unsorted array: Best for dense graphs where EE is close to V2V^2. The O(V2)O(V^2) complexity beats O(ElogV)O(E \log V) when E=Θ(V2)E = \Theta(V^2).
  • Binary min-heap: The practical default. Easy to implement (or use a language's built-in priority queue), and performs well on sparse to moderately dense graphs.
  • Fibonacci heap: Achieves the theoretically best bound of O(VlogV+E)O(V \log V + E), but the constant factors and implementation complexity make it rarely used in practice. It shines on very large sparse graphs where the EE term dominates.

Common Optimizations

Lazy Deletion

Instead of implementing decrease-key (which many standard library priority queues do not support), you can insert duplicate entries and skip stale ones during extraction:

python
1import heapq
2
3def dijkstra(graph, source):
4    dist = {node: float('inf') for node in graph}
5    dist[source] = 0
6    pq = [(0, source)]
7
8    while pq:
9        d, u = heapq.heappop(pq)
10        if d > dist[u]:
11            continue  # skip stale entry
12        for v, weight in graph[u]:
13            new_dist = d + weight
14            if new_dist < dist[v]:
15                dist[v] = new_dist
16                heapq.heappush(pq, (new_dist, v))
17
18    return dist

This approach can insert up to O(E)O(E) entries in the heap, making the complexity O(ElogE)O(E \log E). Since logE2logV\log E \leq 2 \log V for simple graphs, this is still O(ElogV)O(E \log V) asymptotically.

Early Termination

If you only need the shortest path to a single target node (not all nodes), you can stop as soon as that target is extracted from the priority queue. This does not improve worst-case complexity, but it dramatically reduces runtime in practice.

Bidirectional Dijkstra

Run two simultaneous searches, one from the source and one from the target, and stop when the search frontiers meet. This roughly halves the number of nodes explored.

Graph Representation

Using adjacency lists instead of adjacency matrices is critical for sparse graphs. An adjacency matrix requires O(V2)O(V^2) space and O(V)O(V) time to iterate over neighbors, while an adjacency list uses O(V+E)O(V + E) space and O(degree(v))O(\text{degree}(v)) time per vertex.

Important Caveats

  • Dijkstra's algorithm does not work correctly with negative edge weights. For graphs with negative edges, use the Bellman-Ford algorithm with complexity O(VE)O(V \cdot E).
  • For unweighted graphs, BFS achieves O(V+E)O(V + E) and is simpler.
  • In practice, the constant factors of a well-implemented binary heap with lazy deletion often beat the theoretically superior Fibonacci heap.

Summary

ConceptDetails
Core algorithmGreedy shortest-path with priority queue
Binary heap complexityO((V+E)logV)O((V + E) \log V)
Fibonacci heap complexityO(VlogV+E)O(V \log V + E)
Practical defaultBinary min-heap with lazy deletion
Key limitationNo negative edge weights

The binary min-heap implementation of Dijkstra's algorithm strikes the best balance between simplicity and performance for most real-world graphs. Reserve the Fibonacci heap for theoretical analysis or extremely large sparse graphs where the improved O(VlogV+E)O(V \log V + E) bound provides a measurable benefit.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.