Report Content

×

Shortest Path Algorithms: Complete Guide

Master shortest path finding in graphs with Dijkstra's, Bellman-Ford, Floyd-Warshall, and A* algorithms. Learn when to use each method with complexity analysis and real-world applications.

Algorithm Usage in Industry (2023)

Navigation (40%)
Network Routing (25%)
Game AI (20%)
Logistics (15%)

1. Dijkstra's Algorithm

Dijkstra's Shortest Path

Definition

Dijkstra's Algorithm finds the shortest paths from a source node to all other nodes in a weighted graph with non-negative edge weights. It uses a greedy approach with a priority queue to always explore the closest unvisited vertex.

How It Works

1
Initialize distances
  • Set distance to source = 0, all others = ∞
  • Add all vertices to priority queue with their distances
2
Extract minimum vertex
  • Remove vertex with smallest distance from queue
  • If distance is outdated, skip
3
Relax edges
  • For each neighbor, calculate new distance = current + edge weight
  • If new distance < known distance, update and re-add to queue
4
Repeat
  • Continue until queue is empty
  • All shortest paths are found
import heapq

def dijkstra(graph, start):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]
    predecessors = {node: None for node in graph}
    
    while pq:
        current_dist, u = heapq.heappop(pq)
        
        if current_dist > distances[u]:
            continue
        
        for v, weight in graph[u]:
            distance = current_dist + weight
            if distance < distances[v]:
                distances[v] = distance
                predecessors[v] = u
                heapq.heappush(pq, (distance, v))
    
    return distances, predecessors

# Get shortest path from start to target
def get_path(predecessors, target):
    path = []
    while target is not None:
        path.append(target)
        target = predecessors[target]
    return path[::-1]

Complexity: O((V+E) log V) with binary heap, O(V²) with array
Constraints: No negative edge weights
Use When: Single-source shortest paths in non-negative graphs

2. Bellman-Ford Algorithm

Bellman-Ford Shortest Path

Definition

Bellman-Ford Algorithm computes shortest paths from a source vertex to all other vertices in a weighted graph, even when negative edge weights exist. It can also detect negative weight cycles.

How It Works

1
Initialize distances
  • Set distance to source = 0, all others = ∞
  • Store edges in list for easy iteration
2
Relax edges repeatedly
  • Repeat V-1 times: iterate through all edges
  • If dist[u] + weight < dist[v], update dist[v]
3
Check for negative cycles
  • Run one more relaxation pass
  • If any distance improves, negative cycle exists
def bellman_ford(vertices, edges, start):
    distances = {v: float('inf') for v in vertices}
    distances[start] = 0
    predecessors = {v: None for v in vertices}
    
    # Relax edges V-1 times
    for _ in range(len(vertices) - 1):
        for u, v, weight in edges:
            if distances[u] != float('inf') and distances[u] + weight < distances[v]:
                distances[v] = distances[u] + weight
                predecessors[v] = u
    
    # Check for negative cycles
    for u, v, weight in edges:
        if distances[u] != float('inf') and distances[u] + weight < distances[v]:
            raise ValueError("Graph contains negative weight cycle")
    
    return distances, predecessors

# Example usage
vertices = ['A', 'B', 'C', 'D']
edges = [
    ('A', 'B', 4), ('A', 'C', 2),
    ('B', 'C', 1), ('B', 'D', 5),
    ('C', 'D', 8)
]
dist, pred = bellman_ford(vertices, edges, 'A')

Complexity: O(V×E) time, O(V) space
Advantage: Handles negative edges, detects negative cycles
Use When: Graphs with negative edge weights

3. Floyd-Warshall Algorithm

Floyd-Warshall (All-Pairs Shortest Path)

Definition

Floyd-Warshall Algorithm finds shortest paths between all pairs of vertices in a weighted graph. It uses dynamic programming to consider each vertex as a potential intermediate point.

How It Works

1
Initialize distance matrix
  • dist[i][j] = 0 if i == j
  • dist[i][j] = weight(i,j) if edge exists
  • dist[i][j] = ∞ if no direct edge
2
Consider each vertex as intermediate
  • For k from 0 to V-1:
  • For i from 0 to V-1, j from 0 to V-1:
3
Update using dynamic programming
  • If dist[i][k] + dist[k][j] < dist[i][j]:
  • Update dist[i][j] = dist[i][k] + dist[k][j]
def floyd_warshall(graph, n):
    # Initialize distance matrix
    dist = [[float('inf')] * n for _ in range(n)]
    next_node = [[None] * n for _ in range(n)]
    
    for i in range(n):
        dist[i][i] = 0
        next_node[i][i] = i
    
    for u, v, w in graph:
        dist[u][v] = w
        next_node[u][v] = v
    
    # Dynamic programming
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
                    next_node[i][j] = next_node[i][k]
    
    # Check for negative cycles
    for i in range(n):
        if dist[i][i] < 0:
            raise ValueError("Graph contains negative cycle")
    
    return dist, next_node

# Reconstruct path
def get_path(next_node, i, j):
    if next_node[i][j] is None:
        return []
    path = [i]
    while i != j:
        i = next_node[i][j]
        path.append(i)
    return path

Complexity: O(V³) time, O(V²) space
Advantage: Simpler implementation, finds all-pairs
Use When: Dense graphs or when all-pairs needed

4. A* Search Algorithm

A* (A-Star) Pathfinding

Definition

A* Search is an informed graph traversal algorithm that finds the shortest path using a heuristic to guide its search. It combines Dijkstra's algorithm (uniform cost) with Greedy Best-First Search (heuristic).

How It Works

1
Initialize scores
  • gScore: actual cost from start to node
  • fScore = gScore + heuristic(node, goal)
  • Priority queue ordered by fScore
2
Extract best candidate
  • Pop node with smallest fScore
  • If node == goal, reconstruct path
3
Explore neighbors
  • Calculate tentative gScore
  • If better than known, update scores
  • Push neighbors to priority queue
4
Heuristic must be admissible
  • Never overestimates true cost
  • Ensures optimal path (consistent heuristic)
import heapq

def a_star(start, goal, get_neighbors, heuristic):
    open_set = [(0, start)]
    came_from = {}
    g_score = {start: 0}
    f_score = {start: heuristic(start, goal)}
    
    while open_set:
        current_f, current = heapq.heappop(open_set)
        
        if current == goal:
            return reconstruct_path(came_from, current)
        
        for neighbor in get_neighbors(current):
            tentative_g = g_score[current] + get_edge_weight(current, neighbor)
            
            if tentative_g < g_score.get(neighbor, float('inf')):
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g
                f_score[neighbor] = tentative_g + heuristic(neighbor, goal)
                heapq.heappush(open_set, (f_score[neighbor], neighbor))
    
    return None  # No path found

def reconstruct_path(came_from, current):
    path = [current]
    while current in came_from:
        current = came_from[current]
        path.append(current)
    return path[::-1]

# Example: Manhattan heuristic for grid
def manhattan_heuristic(a, b):
    return abs(a[0] - b[0]) + abs(a[1] - b[1])

Complexity: O(E) with good heuristic, worst-case O(b^d)
Heuristic: Must be admissible (never overestimate)
Use When: Pathfinding with domain knowledge (navigation, games)

5. Algorithm Comparison & Selection

Algorithm Properties Comparison

Algorithm Graph Type Negative Edges Negative Cycles Optimal For
Dijkstra's Weighted No No Single-source
Bellman-Ford Weighted Yes Detects Single-source
Floyd-Warshall Weighted Yes Detects All-pairs
A* Weighted No No Single-pair

6. Real-World Applications

GPS Navigation Systems

Dijkstra/A*: Find fastest route between locations

def gps_navigation(graph, start, destination):
    # Use A* with Euclidean distance heuristic
    def heuristic(a, b):
        return haversine_distance(a, b)
    
    return a_star(start, destination, graph, heuristic)

Network Routing (OSPF)

Dijkstra's: Open Shortest Path First protocol

def ospf_routing_table(network_topology, router_id):
    distances, _ = dijkstra(network_topology, router_id)
    return distances

Financial Arbitrage

Bellman-Ford: Detect negative cycles in currency exchange

def detect_arbitrage(exchange_rates):
    # Convert to log negative weights
    graph = convert_to_graph(exchange_rates)
    try:
        distances, _ = bellman_ford(graph, 0)
        return True  # No arbitrage
    except ValueError:
        return False  # Arbitrage opportunity found

Game AI Pathfinding

A*: NPC movement with obstacle avoidance

def game_ai_path(start, goal, obstacles):
    def heuristic(a, b):
        return abs(a[0]-b[0]) + abs(a[1]-b[1])
    
    return a_star(start, goal, get_neighbors(obstacles), heuristic)

Algorithm Selection Guide

Scenario Recommended Algorithm Reason
Road networks (non-negative weights) Dijkstra's with Priority Queue Most efficient for single-source
Financial arbitrage detection Bellman-Ford Detects negative cycles
Small dense graphs (all pairs) Floyd-Warshall Precomputes all paths
Game pathfinding with obstacles A* with Manhattan heuristic Intelligently explores towards goal
Robotics with unknown environment Dijkstra's (no heuristic) Guaranteed optimal path

Shortest Path Implementation Checklist

Choose algorithm based on graph type and requirements
Handle negative weights with Bellman-Ford if needed
Optimize priority queue operations for Dijkstra's
Precompute all-pairs paths if query-heavy (Floyd-Warshall)
Use admissible heuristics for A* optimality
Check for negative cycles in arbitrage problems
Store predecessors for path reconstruction

Pro Tip: For large-scale road networks, combine Dijkstra's with Contraction Hierarchies or ALT heuristics to achieve sub-second query times on continent-scale graphs. For real-time applications, consider bidirectional search which can reduce search space by up to 50%.

You need to be logged in to participate in this discussion.

×
×
×