Shortest Paths

Dijkstra's Algorithm Explained, Step by Step

Dijkstra's algorithm is the workhorse of shortest paths. It powers your GPS, your network routing, and a large share of coding interviews. This guide builds it up from the core idea to a full worked example and clean Python code.

12 Min Read Updated: July 2026 Beginner Friendly
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

What Is Dijkstra's Algorithm?

Dijkstra's algorithm, published by Edsger W. Dijkstra in 1959, finds the shortest path from a single source to every other node in a weighted graph. It works on both directed and undirected graphs, with one firm condition: every edge weight must be non-negative.

Think of the graph as a road network. Nodes are intersections, edges are roads, and each weight is the time or distance to travel that road. Dijkstra's algorithm answers the question every navigation app asks: what is the fastest route from where I am to everywhere else? It is one entry in the broader family covered in shortest path algorithms, and a fixture of the graph theory study roadmap.

The Core Idea

Dijkstra's algorithm is greedy. It keeps a tentative shortest distance to every node, and it repeats one simple move:

Always visit the unvisited node with the smallest known distance, then use it to improve its neighbours.

The insight that makes this correct: because all weights are non-negative, once you pick the closest unvisited node, no future path can ever reach it more cheaply. So the moment a node is chosen, its distance is final. That single guarantee is the whole algorithm.

Concretely, the algorithm maintains:

Improving a neighbour is called relaxation: if the path through the current node is shorter than the neighbour's recorded distance, you lower it.

How It Works, Step by Step

Let's run Dijkstra from node A on this weighted graph. The blue edges will form the final shortest-path tree, and the number beside each node is its final shortest distance from A.

4 2 1 5 8 2 6 3 A B C D E F d=0 d=3 d=2 d=8 d=10 d=13
The shortest-path tree from A (blue). Grey edges exist but are never the cheapest way in.

Here is the trace. At each step we finalize the closest unvisited node (bold) and relax its neighbours. means "not reached yet".

VisitABCDEF
Start0
A (0)042
C (2)03210
B (3)0328
D (8)03281014
E (10)03281013
F (13)03281013

Notice step three: visiting C lowered B from 4 to 3, because the route A → C → B (2 + 1) beats the direct edge A → B (4). That is relaxation doing its job. Watching this unfold on a live graph makes the pattern instant, which you can do in the algorithm visualizer.

Implementation in Python

The clean, interview-ready version uses Python's heapq as the priority queue. The graph is an adjacency list mapping each node to a list of (neighbour, weight) pairs.

import heapq

def dijkstra(graph, start):
    # Every node starts infinitely far away, except the source.
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]  # (distance so far, node)

    while pq:
        dist, node = heapq.heappop(pq)

        # A stale, longer entry for a node we already settled: skip it.
        if dist > distances[node]:
            continue

        for neighbour, weight in graph[node]:
            new_dist = dist + weight
            # Relaxation: found a cheaper way to reach the neighbour.
            if new_dist < distances[neighbour]:
                distances[neighbour] = new_dist
                heapq.heappush(pq, (new_dist, neighbour))

    return distances

Two details matter. First, we push a new entry instead of updating the heap in place, then skip stale entries with the dist > distances[node] check. This "lazy deletion" keeps the code simple and is standard practice. Second, the algorithm naturally computes distances to all nodes; to stop early at a single target, return as soon as you pop it.

Time and Space Complexity

The cost depends on the priority queue. Each edge can trigger at most one push, and each push or pop on a binary heap costs O(log V).

Priority queueTimeBest when
Binary heapO((V + E) log V)The usual choice, sparse graphs
Fibonacci heapO(E + V log V)Dense graphs, theoretical best
Plain arrayO(V²)Very dense graphs

Space is O(V) for the distance table plus the queue. For the reasoning behind these bounds and how they compare across every graph algorithm, see the graph algorithms complexity guide and the one-page cheat sheet.

When Dijkstra Fails: Negative Weights

The greedy guarantee rests entirely on non-negative weights. Add a negative edge and the whole thing can break.

Suppose the algorithm finalizes a node because it looks closest, distance 5. Later it discovers a longer-looking route that passes through a -4 edge and actually reaches that node in 3. Too late: Dijkstra already declared 5 final and moved on. The answer is wrong.

Rule to remember: non-negative weights, use Dijkstra. Any negative weight, use Bellman-Ford, which relaxes every edge repeatedly and can also detect negative cycles.

Dijkstra vs Other Algorithms

Dijkstra is one tool among several. Picking the right one comes down to the graph.

AlgorithmNegative weights?Best forTime
BFSUnweighted onlyUnweighted shortest pathO(V + E)
DijkstraNoNon-negative weightsO((V + E) log V)
Bellman-FordYesNegative weights, cycle checkO(V · E)
A*NoOne target, with a heuristicO(E) typical

The closest relative is A* search, which is Dijkstra plus a heuristic that steers the search toward a single goal. And Dijkstra itself is really breadth-first search upgraded from a plain queue to a priority queue.

Real-World Applications

Watch Dijkstra choose its next node

The priority queue clicks the instant you see it pull the cheapest node, over and over. Run Dijkstra on a live graph, step by step.

Open the Algorithm Visualizer

Frequently Asked Questions

What does Dijkstra's algorithm do?

Dijkstra's algorithm finds the shortest path from a single source node to every other node in a weighted graph, as long as all edge weights are non-negative. It is the standard method behind routing, mapping, and network protocols.

What is the time complexity of Dijkstra's algorithm?

With a binary heap as the priority queue, Dijkstra's algorithm runs in O((V + E) log V) time and O(V) space. With a Fibonacci heap it improves to O(E + V log V), and with a plain array it is O(V squared), which is faster on dense graphs.

Why does Dijkstra's algorithm not work with negative weights?

Dijkstra finalizes each node as soon as it is removed from the priority queue, assuming no cheaper path can appear later. A negative edge breaks that assumption, because a longer route could still lower the total cost. Use Bellman-Ford for graphs with negative weights.

Is Dijkstra's algorithm the same as BFS?

Dijkstra generalizes breadth-first search. BFS uses a plain queue and finds the shortest path in unweighted graphs. Dijkstra replaces the queue with a min-priority queue, so it always expands the closest unvisited node and handles weighted edges.

Further Learning Resources

See It, Don't Just Read It

Dijkstra makes sense the moment you watch the frontier expand. Load a graph, press play, and follow the shortest paths as they form.

Practice with the Algorithm Visualizer