Dijkstra's Algorithm

Find shortest paths in weighted graphs - watch distances update as nodes are relaxed.

Time O((V+E) log V) · Space O(V)

Dijkstra Search Operations

Node
Edge
→
Run
Start
Target
100%
Ctrl + scroll to zoom

Implementation

Python
import heapq
from typing import Dict, List, Any, Tuple


def dijkstra(graph: Dict[Any, Dict[Any, float]], start: Any) -> Dict[Any, float]:
    """
    Find shortest distances from start to all reachable vertices.

    Args:
        graph: Weighted adjacency list {node: {neighbor: weight, ...}}
        start: Starting vertex

    Returns:
        Dictionary mapping each reachable vertex to its shortest distance

    Raises:
        ValueError: If start node is not in graph
    """
    if start not in graph:
        raise ValueError(f"Start node '{start}' not in graph")

    distances = {start: 0}
    priority_queue = [(0, start)]  # (distance, vertex)

    while priority_queue:
        current_dist, current = heapq.heappop(priority_queue)

        # Skip if we've already found a shorter path
        if current_dist > distances.get(current, float("inf")):
            continue

        for neighbor, weight in graph.get(current, {}).items():
            distance = current_dist + weight

            if distance < distances.get(neighbor, float("inf")):
                distances[neighbor] = distance
                heapq.heappush(priority_queue, (distance, neighbor))

    return distances


def dijkstra_with_path(graph: Dict[Any, Dict[Any, float]], start: Any) -> Tuple[Dict[Any, float], Dict[Any, Any]]:
    """
    Find shortest distances and track predecessors for path reconstruction.

    Args:
        graph: Weighted adjacency list {node: {neighbor: weight, ...}}
        start: Starting vertex

    Returns:
        Tuple of (distances dict, predecessors dict)
        - distances: {vertex: shortest_distance}
        - predecessors: {vertex: previous_vertex_on_path}
    """
    if start not in graph:
        raise ValueError(f"Start node '{start}' not in graph")

    distances = {start: 0}
    predecessors: Dict[Any, Any] = {start: None}
    priority_queue = [(0, start)]

    while priority_queue:
        current_dist, current = heapq.heappop(priority_queue)

        if current_dist > distances.get(current, float("inf")):
            continue

        for neighbor, weight in graph.get(current, {}).items():
            distance = current_dist + weight

            if distance < distances.get(neighbor, float("inf")):
                distances[neighbor] = distance
                predecessors[neighbor] = current
                heapq.heappush(priority_queue, (distance, neighbor))

    return distances, predecessors


def shortest_path(graph: Dict[Any, Dict[Any, float]], start: Any, target: Any) -> Tuple[List[Any] | None, float]:
    """
    Find the shortest path and its total weight between two vertices.

    Args:
        graph: Weighted adjacency list {node: {neighbor: weight, ...}}
        start: Starting vertex
        target: Target vertex

    Returns:
        Tuple of (path as list of vertices, total distance)
        Returns (None, inf) if no path exists
    """
    if start not in graph:
        raise ValueError(f"Start node '{start}' not in graph")
    if target not in graph:
        raise ValueError(f"Target node '{target}' not in graph")

    if start == target:
        return [start], 0

    distances, predecessors = dijkstra_with_path(graph, start)

    if target not in distances:
        return None, float("inf")

    # Reconstruct path by backtracking from target
    path = []
    current = target
    while current is not None:
        path.append(current)
        current = predecessors[current]

    return path[::-1], distances[target]


# Example usage and demonstration
if __name__ == "__main__":
    # Example weighted graph
    #
    #       A ---4--- B
    #       |         |
    #       2         1
    #       |         |
    #       C ---3--- D ---5--- E
    #
    example_graph = {
        "A": {"B": 4, "C": 2},
        "B": {"A": 4, "D": 1},
        "C": {"A": 2, "D": 3},
        "D": {"B": 1, "C": 3, "E": 5},
        "E": {"D": 5},
    }

    print("Distances from A:", dijkstra(example_graph, "A"))

    distances, predecessors = dijkstra_with_path(example_graph, "A")
    print("Distances:", distances)
    print("Predecessors:", predecessors)

    path, distance = shortest_path(example_graph, "A", "E")
    print(f"Shortest path A->E: {path} (distance: {distance})")

    path, distance = shortest_path(example_graph, "A", "D")
    print(f"Shortest path A->D: {path} (distance: {distance})")
About Dijkstra's Algorithm

How Dijkstra's Algorithm Works

Dijkstra's algorithm finds the shortest path from a source node to all other nodes in a weighted graph with non-negative edge weights. It uses a priority queue to always process the closest unvisited node, guaranteeing optimal paths.

Initialize distances to infinity except the source (0). Use a priority queue to repeatedly extract the node with the smallest tentative distance. For each neighbor, check if going through the current node offers a shorter path (relaxation). Update if so.

Complexity

Time
O((V+E) log V)
Space
O(V)

When to Use Dijkstra's Algorithm

  • GPS and navigation systems for route planning
  • Network routing protocols (OSPF)
  • Any shortest-path problem with non-negative weights