Video 1: Dijkstras Shortest Path Algorithm Explained | With Example | Graph Theory
Channel: FelixTechTips
Full video link:
https://www.youtube.com/watch?v=bZkzH5x0SKUAlgorithm • 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
| Metric | Value |
|---|---|
| Complexity Note 1 | O((V + E) log V) with heap |
Prefer video learning? This explainer gives a quick visual walkthrough of the core idea before you dive into the detailed sections below.
Video 1: Dijkstras Shortest Path Algorithm Explained | With Example | Graph Theory
Channel: FelixTechTips
Full video link:
https://www.youtube.com/watch?v=bZkzH5x0SKUVideo 2: Dijkstra's Algorithm - Computerphile
Channel: Computerphile
Full video link:
https://www.youtube.com/watch?v=GazC3A4OQTEVideo 3: Dijkstra's Algorithm - Single Source Shortest Path - Greedy Method
Channel: Shradha Khapra
Full video link:
https://www.youtube.com/watch?v=8gYBHjtjWBIVideo 4: Shortest Path Algorithms Explained (Dijkstra's & Bellman-Ford)
Channel: b001
Full video link:
https://www.youtube.com/watch?v=j0OUwduDOS0Track how the min-priority frontier relaxes weighted paths from the source.
Initialize source distance to zero and others to infinity.
Source
A
Distances
A:0, B:∞, C:∞, D:∞, E:∞
Priority Queue
[(0,A)]
Settled Nodes
{}
Learn the core building blocks and terminology in one place before comparisons, so the mechanics are clear and duplicates are removed.
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.
This walkthrough connects the core concepts of Dijkstra Shortest Path into one end-to-end execution flow.
Step 1
Best-known distance from source to each node.
Before
dist
After
Transition
Why this step matters: Distance array is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.
Step 2
Frontier by smallest current distance.
Before
After
dist
Transition
Why this step matters: Priority queue is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.
Step 3
Try improving neighbor distance via current node.
Before
After
Transition
Why this step matters: Relaxation is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.
Step 4
Optional optimization to skip stale states.
Before
After
Transition
Why this step matters: Visited/finalized set is a required building block for understanding how Dijkstra Shortest Path stays correct and performant on large inputs.
vs BFS
When to choose this: Choose Dijkstra when edge weights differ.
Tradeoff: BFS is faster/simpler for unweighted graphs.
vs Bellman-Ford
When to choose this: Choose Dijkstra for non-negative weights and better performance.
Tradeoff: Bellman-Ford supports negative edges at higher cost.
vs A*
When to choose this: Choose Dijkstra when no admissible heuristic is available.
Tradeoff: A* can be faster toward single target with good heuristic.
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.
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
}
Use these signals to decide if this data structure/algorithm is the right fit before implementation.
| # | Problem | Difficulty | Typical Complexity |
|---|---|---|---|
| 1 | Network Delay Time | Medium | O((V+E) log V) |
| 2 | Path With Minimum Effort | Medium | O((V+E) log V) |
| 3 | Cheapest Flights Within K Stops | Medium | Varies |
| 4 | Number of Ways to Arrive at Destination | Medium | O((V+E) log V) |
| 5 | The Maze II | Medium | O((V+E) log V) |
| 6 | Swim in Rising Water | Hard | O(n^2 log n) |
| 7 | Minimum Cost to Reach Destination in Time | Hard | O((V+E) log V) |
| 8 | Reachable Nodes In Subdivided Graph | Hard | O((V+E) log V) |
| 9 | Minimum Weighted Subgraph With the Required Paths | Hard | O((V+E) log V) |
| 10 | Second Minimum Time to Reach Destination | Hard | Graph shortest-path variant |