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.
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 time, where 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 . Since there are extractions total, this contributes .
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 for a binary min-heap.
In the worst case, every edge triggers a decrease-key, so this contributes , where is the number of edges.
Overall Complexity
Combining both components:
Since most practical graphs have (a connected graph has at least edges), this simplifies to in practice.
Comparison of Priority Queue Implementations
Different priority queue choices lead to different complexities:
| Priority Queue Type | Extract-Min | Decrease-Key | Overall Dijkstra |
| Unsorted array | |||
| Binary min-heap | |||
| Fibonacci heap | amortized | amortized |
When to Use Which
- Unsorted array: Best for dense graphs where is close to . The complexity beats when .
- 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 , but the constant factors and implementation complexity make it rarely used in practice. It shines on very large sparse graphs where the 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:
This approach can insert up to entries in the heap, making the complexity . Since for simple graphs, this is still 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 space and time to iterate over neighbors, while an adjacency list uses space and 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 .
- For unweighted graphs, BFS achieves 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
| Concept | Details |
| Core algorithm | Greedy shortest-path with priority queue |
| Binary heap complexity | |
| Fibonacci heap complexity | |
| Practical default | Binary min-heap with lazy deletion |
| Key limitation | No 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 bound provides a measurable benefit.
Related reading
- Time complexity of a Priority Queue in C
- Time complexity of a recursive algorithm
- Time complexity of adjacency list representation?
- Time Complexity of an Algorithm Nested Loops
- Time complexity of depth-first graph algorithm
- Time Complexity of the Kruskal Algorithm?
- Time complexity of Euclid's Algorithm
- Time complexity of fun?

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 courseTrack 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.