Dijkstra's Algorithm

Graphs
avg O(E log V)

Dijkstra's algorithm finds the cheapest route from one start node to every other node in a weighted graph. It grows a settled region outward, always locking in the closest unsettled node next - because no undiscovered path could ever reach that node more cheaply. This cheapest-first rule is what your GPS runs (in souped-up form) every time you ask for directions. Think of it as BFS for weighted worlds: BFS counts hops, Dijkstra adds up costs.

Watch it settle the cheapest first

Press play - or drag the timeline and step through it yourself.
Settled 1Relaxations 0Step 1 / 38
102344716262ABCDEFG

Find the cheapest route from A to every node. Edge numbers are costs. The rule: always settle the closest unsettled node - its cost can never improve again.

being settled has a tentative cost settled - cost is final best route so far not cheaper

Complexity

Best
O(E log V)
Input already in order
Average
O(E log V)
Normal mixed input
Worst
O(E log V)
Worst possible input
Space
O(V)
Extra memory used

A road trip with a notebook

You are in city A and want the cheapest travel cost to every city on the map. In a notebook you write the best price you know so far for each city - at first only A costs 0, everything else is unknown. Now repeat one move: find the cheapest uncircled price in the notebook and circle it. That price is final, because any other way into that city would have to pass through somewhere already more expensive. Then check the circled city's direct roads: does going through it make any neighbour cheaper? If yes, cross out the old price and write the better one. When every city is circled, the notebook holds the true cheapest cost to everywhere.

How it works, step by step

  1. Give every node a tentative distance: 0 for the start, ∞ for everyone else.

  2. Pick the unsettled node with the smallest tentative distance and settle it. Its distance is now final.

  3. Why is that safe? Any other route into it must leave the settled region through a node that is already at least as far away - and with no negative weights, the route cannot get cheaper from there.

  4. Relax each edge leaving the settled node: if `dist[u] + weight < dist[v]`, you found a cheaper way to v - update its distance and remember the edge you came through.

  5. Repeat until every node is settled. The remembered edges form a shortest-path tree: one best route from the start to every node.

  6. With a priority queue doing the "pick the smallest" step, the total work is O(E log V).

The code, in JavaScript

The honest, readable version: a plain linear scan finds the closest unsettled node each round. Up to a few thousand nodes this is genuinely fine - and there is nothing in it to get wrong.

javascript
function dijkstra(graph, start) {
  const dist = {};
  const settled = new Set();
  for (const node in graph) dist[node] = Infinity;
  dist[start] = 0;

  for (let i = 0; i < Object.keys(graph).length; i++) {
    // Linear scan: the unsettled node with the smallest tentative distance.
    let u = null;
    for (const node in graph) {
      if (!settled.has(node) && (u === null || dist[node] < dist[u])) u = node;
    }
    if (dist[u] === Infinity) break;   // everything left is unreachable
    settled.add(u);                    // dist[u] is final from here on

    for (const [v, w] of graph[u]) {
      if (dist[u] + w < dist[v]) {
        dist[v] = dist[u] + w;         // relax: found a cheaper way to v
      }
    }
  }
  return dist;
}

const graph = {
  A: [["B", 10], ["C", 2]],
  B: [["A", 10], ["C", 3], ["D", 4]],
  C: [["A", 2], ["B", 3], ["D", 4], ["E", 7]],
  D: [["B", 4], ["C", 4], ["E", 1], ["F", 6]],
  E: [["C", 7], ["D", 1], ["F", 2], ["G", 6]],
  F: [["D", 6], ["E", 2], ["G", 2]],
  G: [["E", 6], ["F", 2]],
};

dijkstra(graph, "A");
// → { A: 0, B: 5, C: 2, D: 6, E: 7, F: 9, G: 11 }
// Note B: the 2-hop route A→C→B (2+3=5) beats the direct A→B road (10).

Dry run: Dijkstra from node A

The same steps the visualizer plays, written as a table. Generated by running the real algorithm - not written by hand.

StepSettledWhat happened
11A is the closest unsettled node (d=0). Settle it and relax its edges.
21First route to B: d=10, via edge A-B.
31First route to C: d=2, via edge A-C.
42The closest unsettled node is C (d=2), not B (d=10). Cheapest first - settle C, its cost is final.
52Found a cheaper way to B: 10 → 5. Two hops through C beat the direct road from A.
62First route to D: d=6, via edge C-D.
72First route to E: d=9, via edge C-E.
83The closest unsettled node is B (d=5). Settle it - no cheaper route to B can exist.
939 is not cheaper than 6 - D keeps its current route.
104The closest unsettled node is D (d=6). Settle it - no cheaper route to D can exist.
114Found a cheaper way to E: 9 → 7. E's route now comes through D.
124First route to F: d=12, via edge D-F.
135The closest unsettled node is E (d=7). Settle it - no cheaper route to E can exist.
145Found a cheaper way to F: 12 → 9. F's route now comes through E.
155First route to G: d=13, via edge E-G.
166The closest unsettled node is F (d=9). Settle it - no cheaper route to F can exist.
176Found a cheaper way to G: 13 → 11. G's route now comes through F.
187G is the last node left (d=11). Settle it.

Good choice when…

  • You need the cheapest route and edge costs are non-negative: roads, network latency, fares, travel time.
  • One start, many destinations - a single run gives the cheapest cost to every node, which is how GPS and routing tables use it.
  • Edge costs differ. The moment weights are unequal, BFS's fewest-hops answer stops being the cheapest one.
  • You only care about one target: stop the moment it settles, and skip the rest of the graph for free.

Bad choice when…

  • Any edge weight can be negative - the settled-is-final promise breaks. That is Bellman-Ford's job.
  • All edges cost the same. Plain BFS gives the identical answer with a plain queue and less machinery.
  • You want the cheapest way to wire all nodes together (cables, pipes). That is a minimum spanning tree - Kruskal - not a shortest-path tree; the two trees can differ.

Common mistakes

  • One negative edge quietly breaks the settled-is-final promise. Take directed edges A→B=2, B→D=2, A→C=5, C→B=-4. Dijkstra settles B at 2 and computes D=4. The cheaper way to B (via C, cost 1) is found too late: B is already settled, so D is never re-checked. The true cost of D is 1+2=3. Negative weights are Bellman-Ford's job.
  • JavaScript has no built-in priority queue, so people improvise: `queue.sort()` every iteration, or a minimum-scan of an array inside the main loop. It returns correct answers - and quietly ships the O(V²) version. Fine at 1,000 nodes, painful at 1,000,000.
  • You do not need decrease-key. Textbook heaps update a node's priority in place, which is fiddly to code. The practical trick is lazy deletion: on improvement just push a new [dist, node] pair and skip stale pairs on pop. The heap grows to O(E) entries, but O(E log E) is still O(E log V).
  • With lazy deletion, forgetting `if (d > dist[u]) continue` still returns correct distances - which is why nobody notices. But every stale pop re-relaxes all of u's edges and pushes even more duplicates. The complexity quietly inflates, and on dense graphs it shows up as mystery slowness.

Dijkstra's Algorithm vs. its closest relatives

AlgorithmBestAverageWorstSpaceStable
Dijkstra's Algorithmthis pageO(E log V)O(E log V)O(E log V)O(V)-
Breadth-First SearchO(V + E)O(V + E)O(V + E)O(V)-
Bellman–FordO(E)O(V·E)O(V·E)O(V)-
Kruskal's MSTO(E log E)O(E log E)O(E log E)O(V)-