Back to Data Structures and Algorithms

Dijkstra Shortest Path Complete Guide

Algorithm • extra hard

Dijkstra computes shortest paths from one source in graphs with non-negative edge weights.

It repeatedly picks the currently cheapest frontier node using a min-priority queue.

When weights can be negative, use Bellman-Ford or other alternatives instead.

Typical Complexity Baseline

MetricValue
Complexity Note 1O((V + E) log V) with heap

See It

Concept Diagram

A small animated SVG that illustrates the core idea the moving parts you need to keep in your head when you read the rest of the guide.

Dijkstra shortest path in weighted graph

The greedy choice always relaxes the closest unvisited node. Highlighted edges form the shortest path.

greedy: relax the closest unvisited node next2534261352Sd=0Ad=Bd=Cd=Dd=Ed=Td=
Step 1/9Init: dist[S] = 0, every other node = ∞.

Watch First

Video Explainer

A quick visual walkthrough before the detailed sections pick the format that helps you learn fastest.

Video 1: Dijkstras Shortest Path Algorithm Explained | With Example | Graph Theory

Channel: FelixTechTips

Watch on YouTube →

Video 3: Dijkstra's Algorithm - Single Source Shortest Path - Greedy Method

Channel: Shradha Khapra

Watch on YouTube →

Video 4: Shortest Path Algorithms Explained (Dijkstra's & Bellman-Ford)

Channel: b001

Watch on YouTube →

Mechanics

Core Concepts

The building blocks and terminology you need before everything else clicks. Skim or deep-read.

Distance array

What it is Best-known distance from source to each node.

Why it matters Distance array is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.

Priority queue

What it is Frontier by smallest current distance.

Why it matters Priority queue is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.

Relaxation

What it is Try improving neighbor distance via current node. Update distance if new path is cheaper.

Why it matters Relaxation is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs. Update distance if new path is cheaper. In Dijkstra Shortest Path, this definition helps you reason about correctness and complexity when inputs scale.

Visited/finalized set

What it is Optional optimization to skip stale states.

Why it matters Visited/finalized set is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.

Adjacency list

What it is Outgoing weighted edges per node.

Why it matters Adjacency list is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.

Stale heap entry

What it is Queue entry whose distance no longer matches best known value.

Why it matters Queue entry whose distance no longer matches best known value. In Dijkstra Shortest Path, this definition helps you reason about correctness and complexity when inputs scale.

Non-negative weight

What it is Edge cost >= 0 required for Dijkstra correctness.

Why it matters Edge cost >= 0 required for Dijkstra correctness. In Dijkstra Shortest Path, this definition helps you reason about correctness and complexity when inputs scale.

Single-source shortest path

What it is Shortest paths from one source to all nodes.

Why it matters Shortest paths from one source to all nodes. In Dijkstra Shortest Path, this definition helps you reason about correctness and complexity when inputs scale.

Shortest path tree

What it is Parent pointers describing chosen shortest paths.

Why it matters Parent pointers describing chosen shortest paths. In Dijkstra Shortest Path, this definition helps you reason about correctness and complexity when inputs scale.

Walkthrough

Putting It All Together

Every frame of the concept diagram, laid out top-to-bottom so you can scroll through the algorithm at your own pace.

Step 1/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=Bd=Cd=Dd=Ed=Td=

Init: dist[S] = 0, every other node = ∞.

Step 2/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=Dd=Ed=Td=

Visit S (d=0). Relax → A→2, B→5.

Step 3/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=Td=

Visit A (d=2). Relax → C→5, D→6.

Step 4/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=11Td=

Visit B (d=5). Relax → E→11.

Step 5/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=8Td=

Visit C (d=5). Relax → E→8.

Step 6/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=8Td=11

Visit D (d=6). Relax → T→11.

Step 7/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=8Td=10

Visit E (d=8). Relax → T→10.

Step 8/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=8Td=10

Visit T (d=10). No improvements.

Step 9/9
greedy: relax the closest unvisited node next2534261352Sd=0Ad=2Bd=5Cd=5Dd=6Ed=8Td=10

Done. Shortest path S → T has total cost 10.

Tradeoffs

How It Compares

When this technique wins, when it loses, and what to reach for instead.

vs. BFS

Choose this when Choose Dijkstra when edge weights differ.

Tradeoff BFS is faster/simpler for unweighted graphs.

vs. Bellman-Ford

Choose this when Choose Dijkstra for non-negative weights and better performance.

Tradeoff Bellman-Ford supports negative edges at higher cost.

vs. A*

Choose this when Choose Dijkstra when no admissible heuristic is available.

Tradeoff A* can be faster toward single target with good heuristic.

In Production

Real-World Stories

Where this shows up at real companies and what the on-call engineer learned the hard way.

Google Maps

Route planning systems rely on shortest-path families including Dijkstra-style expansion phases.

Takeaway Weighted graph pathing is core in location products.

Network routers

Link-state routing protocols historically use Dijkstra-like SPF computations.

Takeaway Internet infrastructure uses shortest-path algorithms in control planes.

Ride-hailing platforms

ETA and routing services evaluate weighted travel graphs continuously.

Takeaway Path-cost optimization directly affects product quality.

Code

Implementation

A clean reference implementation in TypeScript, Python, and Go. Switch languages with the toggle.

Use min-priority frontier and relax edges from cheapest-known node each step.

Complexity: O((V+E) log V) with binary heap

Dijkstra shortest distances

type WeightedEdge = { edgeWeight: number; neighborNode: number }

type HeapNode = { distance: number; node: number }

class MinDistanceHeap {
  private readonly values: HeapNode[] = []

  push(params: HeapNode): void {
    this.values.push(params)
    this.bubbleUp(this.values.length - 1)
  }

  pop(): HeapNode | null {
    if (this.values.length === 0) return null
    const rootValue = this.values[0]
    const lastValue = this.values.pop()
    if (this.values.length > 0 && lastValue) {
      this.values[0] = lastValue
      this.bubbleDown(0)
    }
    return rootValue
  }

  private bubbleUp(startIndex: number): void {
    let currentIndex = startIndex
    while (currentIndex > 0) {
      const parentIndex = Math.floor((currentIndex - 1) / 2)
      if (this.values[parentIndex].distance <= this.values[currentIndex].distance) break
      ;[this.values[parentIndex], this.values[currentIndex]] = [this.values[currentIndex], this.values[parentIndex]]
      currentIndex = parentIndex
    }
  }

  private bubbleDown(startIndex: number): void {
    let currentIndex = startIndex
    while (true) {
      const leftChildIndex = currentIndex * 2 + 1
      const rightChildIndex = currentIndex * 2 + 2
      let smallestIndex = currentIndex

      if (leftChildIndex < this.values.length && this.values[leftChildIndex].distance < this.values[smallestIndex].distance) {
        smallestIndex = leftChildIndex
      }
      if (rightChildIndex < this.values.length && this.values[rightChildIndex].distance < this.values[smallestIndex].distance) {
        smallestIndex = rightChildIndex
      }
      if (smallestIndex === currentIndex) break

      ;[this.values[currentIndex], this.values[smallestIndex]] = [this.values[smallestIndex], this.values[currentIndex]]
      currentIndex = smallestIndex
    }
  }
}

function dijkstraShortestDistances(params: { adjacencyByNode: WeightedEdge[][]; sourceNode: number }): number[] {
  const distanceByNode = new Array<number>(params.adjacencyByNode.length).fill(Number.POSITIVE_INFINITY)
  distanceByNode[params.sourceNode] = 0

  const frontierHeap = new MinDistanceHeap()
  frontierHeap.push({ distance: 0, node: params.sourceNode })

  while (true) {
    const nextEntry = frontierHeap.pop()
    if (!nextEntry) break

    if (nextEntry.distance !== distanceByNode[nextEntry.node]) {
      continue
    }

    for (const edge of params.adjacencyByNode[nextEntry.node]) {
      const candidateDistance = nextEntry.distance + edge.edgeWeight
      if (candidateDistance < distanceByNode[edge.neighborNode]) {
        distanceByNode[edge.neighborNode] = candidateDistance
        frontierHeap.push({ distance: candidateDistance, node: edge.neighborNode })
      }
    }
  }

  return distanceByNode
}

Watch Out

Common Problems and Failure Modes

The bugs and edge cases that bite engineers most often. Skim before you ship.

  • Using Dijkstra on graphs with negative edges.
  • Not skipping stale priority queue entries.
  • Incorrect relaxation condition.
  • Initializing distances incorrectly.
  • Assuming path reconstruction without storing parent pointers.

Pro Tips

Tips and Tricks

Small habits that pay off every time you reach for this pattern.

  • Dijkstra requires non-negative edge weights; verify this before coding.
  • Use min-heap and skip stale entries to keep complexity under control.
  • Store parent pointers when path reconstruction is required, not only distances.
  • For unweighted graphs, BFS is simpler and often faster.

Pattern Recognition

When to Use

Concrete signals that tell you this is the right pattern both at work and on LeetCode.

Real-system usage signals

  • Reliable shortest-path results for weighted non-negative graphs.
  • Efficient with adjacency list + heap for sparse graphs.
  • Applicable to routing, scheduling, and cost optimization problems.

LeetCode-specific tips (pattern-identification signals)

  • Identification signal: shortest path on weighted graph with non-negative edge weights.
  • Identification signal: you repeatedly expand the currently cheapest-known frontier node.
  • If edges have weights and BFS no longer works correctly, Dijkstra is often the next step.
  • For Dijkstra Shortest Path questions, start by naming the core invariant before writing code.
  • Use the constraint section to set time/space target first, then pick the data structure/algorithm.
  • Solve one tiny example by hand and map each transition to your variables before implementing.
  • Run adversarial cases: empty input, duplicates, max-size input, and sorted/reverse patterns when relevant.
  • During interviews, explain why your approach is the right pattern for this prompt, not just why the code works.

Practice

LeetCode Progression

A curated easy-to-hard problem ladder. Each one reinforces a specific aspect of the pattern.

#ProblemDifficultyTypical Complexity
1Network Delay TimeMediumO((V+E) log V)
2Path With Minimum EffortMediumO((V+E) log V)
3Cheapest Flights Within K StopsMediumVaries
4Number of Ways to Arrive at DestinationMediumO((V+E) log V)
5The Maze IIMediumO((V+E) log V)
6Swim in Rising WaterHardO(n^2 log n)
7Minimum Cost to Reach Destination in TimeHardO((V+E) log V)
8Reachable Nodes In Subdivided GraphHardO((V+E) log V)
9Minimum Weighted Subgraph With the Required PathsHardO((V+E) log V)
10Second Minimum Time to Reach DestinationHardGraph shortest-path variant