Skip to content

Dijkstra's Algorithm — Shortest Path Guide

DodaTech Updated 2026-06-24 6 min read

In this tutorial, you'll learn about Dijkstra's Algorithm. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Dijkstra's algorithm finds the shortest paths from a source vertex to all other vertices in a weighted graph with non-negative edge weights. It uses a priority queue to always expand the closest unvisited vertex.

What You'll Learn

You will learn Dijkstra's algorithm with a priority queue, how to reconstruct shortest paths, handling of edge cases, and why it fails with negative weights.

Why It Matters

Dijkstra's algorithm powers GPS navigation, network routing protocols (OSPF), social network recommendation engines, and any system requiring optimal pathfinding in weighted graphs.

Real-World Use

Durga Antivirus Pro uses Dijkstra's algorithm for network threat propagation analysis. When a threat is detected on one machine, the algorithm finds the shortest infection path through the network topology to identify which systems were likely compromised first.

Dijkstra's Algorithm Flow

graph LR
    A((A)) --4--> B((B))
    A --2--> C((C))
    B --1--> C
    B --5--> D((D))
    C --8--> D
    C --10--> E((E))
    D --2--> E
    style A fill:#4a90d9,stroke:#333,color:#fff
    style B fill:#e67e22,stroke:#333,color:#fff
    style C fill:#e67e22,stroke:#333,color:#fff
    style D fill:#e67e22,stroke:#333,color:#fff
    style E fill:#e67e22,stroke:#333,color:#fff

From A: shortest distances are A(0), C(2), B(3), D(8), E(10).

Implementation with Priority Queue

import heapq

def dijkstra(graph, start):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]
    previous = {node: None for node in graph}

    while pq:
        current_dist, current = heapq.heappop(pq)
        if current_dist > distances[current]:
            continue
        for neighbor, weight in graph[current]:
            distance = current_dist + weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                previous[neighbor] = current
                heapq.heappush(pq, (distance, neighbor))

    return distances, previous

def reconstruct_path(previous, start, end):
    path = []
    curr = end
    while curr is not None:
        path.append(curr)
        curr = previous[curr]
    path.reverse()
    return path if path[0] == start else []

graph = {
    'A': [('B', 4), ('C', 2)],
    'B': [('C', 1), ('D', 5)],
    'C': [('B', 1), ('D', 8), ('E', 10)],
    'D': [('E', 2)],
    'E': []
}

distances, previous = dijkstra(graph, 'A')
print("Distances:", distances)
print("Path A -> E:", reconstruct_path(previous, 'A', 'E'))

Expected output:

Distances: {'A': 0, 'B': 3, 'C': 2, 'D': 8, 'E': 10}
Path A -> E: ['A', 'C', 'B', 'D', 'E']
function dijkstra(graph, start) {
    const distances = {};
    const previous = {};
    for (let node in graph) {
        distances[node] = Infinity;
        previous[node] = null;
    }
    distances[start] = 0;

    const pq = [[0, start]];
    while (pq.length) {
        pq.sort((a, b) => a[0] - b[0]);
        const [currentDist, current] = pq.shift();
        if (currentDist > distances[current]) continue;
        for (let [neighbor, weight] of graph[current]) {
            const distance = currentDist + weight;
            if (distance < distances[neighbor]) {
                distances[neighbor] = distance;
                previous[neighbor] = current;
                pq.push([distance, neighbor]);
            }
        }
    }
    return { distances, previous };
}

function reconstructPath(previous, start, end) {
    const path = [];
    let curr = end;
    while (curr !== null) {
        path.push(curr);
        curr = previous[curr];
    }
    path.reverse();
    return path[0] === start ? path : [];
}

const graph = {
    'A': [['B', 4], ['C', 2]],
    'B': [['C', 1], ['D', 5]],
    'C': [['B', 1], ['D', 8], ['E', 10]],
    'D': [['E', 2]],
    'E': []
};

const { distances, previous } = dijkstra(graph, 'A');
console.log("Distances:", distances);
console.log("Path A -> E:", reconstructPath(previous, 'A', 'E'));

Expected output:

Distances: { A: 0, B: 3, C: 2, D: 8, E: 10 }
Path A -> E: [ 'A', 'C', 'B', 'D', 'E' ]

Efficient Priority Queue Implementation

JavaScript's Array.shift() is O(n). A proper binary heap gives O(log n) per operation:

# Python's heapq already uses a binary heap — O(log n) push/pop
# This is the recommended implementation
import heapq

def dijkstra_fast(graph, start):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]

    while pq:
        current_dist, current = heapq.heappop(pq)
        if current_dist > distances[current]:
            continue
        for neighbor, weight in graph[current]:
            distance = current_dist + weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heapq.heappush(pq, (distance, neighbor))

    return distances

Finding the Shortest Path to a Target

Early termination when the target is reached:

def dijkstra_to_target(graph, start, end):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]
    previous = {node: None for node in graph}

    while pq:
        current_dist, current = heapq.heappop(pq)
        if current == end:
            return reconstruct_path(previous, start, end)
        if current_dist > distances[current]:
            continue
        for neighbor, weight in graph[current]:
            distance = current_dist + weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                previous[neighbor] = current
                heapq.heappush(pq, (distance, neighbor))

    return None

Complexity Analysis

Aspect Value
Time (binary heap) O((V + E) log V)
Time (array) O(V²)
Space O(V)
Edge weights Must be non-negative

Dijkstra with Negative Weights

Dijkstra's algorithm fails with negative edges. Once a node is finalized (popped from the PQ), its distance is never updated. A negative edge to a finalized node would reduce its distance, but the algorithm doesn't revisit it.

For negative weights, use Bellman-Ford algorithm (O(VE)).

Common Pitfalls

1. Not Using a Priority Queue

Linear search for the minimum distance node makes the algorithm O(V²). Always use a binary heap for sparse graphs.

2. Forgetting the if current_dist > distances[current] Check

Stale entries in the priority queue must be skipped. Without this check, outdated distances could incorrectly update neighbors.

3. Using Dijkstra on Graphs with Negative Edges

Dijkstra assumes all edge weights are non-negative. Negative edges can cause incorrect results or infinite loops.

4. Not Handling Unreachable Nodes

When a node is unreachable, its distance remains infinity. Code must handle this before path reconstruction.

5. Integer Overflow for Large Distances

Use float('inf') in Python or Infinity in JavaScript. In languages with fixed integers, reserve a sentinel value.

Practice Questions

1. Find the shortest path in a grid with weighted cells using Dijkstra.

def dijkstra_grid(grid):
    rows, cols = len(grid), len(grid[0])
    distances = [[float('inf')] * cols for _ in range(rows)]
    distances[0][0] = grid[0][0]
    pq = [(grid[0][0], 0, 0)]
    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]

    while pq:
        dist, r, c = heapq.heappop(pq)
        if dist > distances[r][c]:
            continue
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            if 0 <= nr < rows and 0 <= nc < cols:
                new_dist = dist + grid[nr][nc]
                if new_dist < distances[nr][nc]:
                    distances[nr][nc] = new_dist
                    heapq.heappush(pq, (new_dist, nr, nc))
    return distances[rows - 1][cols - 1]

2. Find the cheapest flight with at most k stops.

3. Network delay time: find the time for a signal to reach all nodes in a network.

4. Challenge: Implement the A* algorithm, which extends Dijkstra with a heuristic to guide search toward the goal. Compare A* with Dijkstra on a grid.

5. Real-world task: Doda Browser's download manager calculates optimal routing for downloading large files from multiple mirrors. Write a function that models mirrors as graph nodes with latency weights and uses Dijkstra to find the fastest download path.

What's Next

Dynamic Programming — Memoization & Tabulation
BFS — Graph Traversal Guide
DFS — Graph Traversal Guide

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro