After learning how to represent graphs and traverse them using Breadth-First Search (BFS) and Depth-First Search (DFS), we turn our attention to another fundamental graph problem: finding the shortest path between nodes. This capability is essential in many machine learning contexts, such as analyzing influence spread in social networks, optimizing routes in logistics, finding semantic similarity between concepts represented in a graph, or even understanding connections within complex biological networks.
The "shortest path" depends on what we mean by "short".
Unweighted Graphs: In a graph where edges don't have assigned weights or costs, the shortest path is simply the one with the fewest edges. As we saw when discussing graph traversal, BFS naturally finds the shortest path from a source node to all other reachable nodes in terms of edge count. This is because BFS explores the graph layer by layer, guaranteeing that the first time we reach a node, it's via the path with the minimum number of edges.
Weighted Graphs: Real-world scenarios often involve costs, distances, or probabilities associated with traversing an edge. In these weighted graphs, the shortest path between two nodes is the path where the sum of the weights of the edges along the path is minimized. Finding this path requires more sophisticated algorithms than BFS.
Let's consider a simple weighted graph:
A simple weighted, undirected graph. Numbers on edges represent weights or costs.
In the graph above, the shortest path from A to E is A -> C -> B -> E with a total weight of 2+1+3=6. The path A -> B -> E has weight 4+3=7, and A -> C -> D -> E has weight 2+1+5=8.
For finding the shortest path in weighted graphs where all edge weights are non-negative, Dijkstra's algorithm is a cornerstone technique. It finds the shortest paths from a single source node to all other nodes in the graph.
The core idea behind Dijkstra's is to maintain a set of visited nodes (for which the shortest path from the source is known) and tentatively calculated distances to unvisited nodes. It iteratively selects the unvisited node with the smallest tentative distance, marks it as visited, and updates the tentative distances of its unvisited neighbors. This process relies heavily on a priority queue (often implemented using a heap, as discussed in Chapter 5) to efficiently retrieve the unvisited node with the minimum distance.
Algorithm Steps (High-Level):
u
with the smallest tentative distance from the priority queue. Mark u
as visited.v
of u
:
v
through u
: distance(source, u) + weight(u, v)
.v
, update v
's distance and potentially update its position in the priority queue.Performance: With an efficient priority queue implementation (like a binary heap), Dijkstra's algorithm typically runs in O(E+VlogV) time, where V is the number of vertices and E is the number of edges.
Limitation: Dijkstra's algorithm guarantees optimality only when edge weights are non-negative. It can produce incorrect results if the graph contains negative edge weights because revisiting a node via a negative edge might lead to a shorter path than previously found, violating the algorithm's assumption.
While Dijkstra's is fundamental, other algorithms address different variations of the shortest path problem:
Shortest path algorithms are not just theoretical tools; they find practical use in ML:
Understanding the principles behind finding shortest paths in graphs, especially in weighted scenarios using algorithms like Dijkstra's, equips you to analyze relational data more effectively and leverage graph structures in your machine learning pipelines. Knowing the trade-offs between different algorithms allows you to choose the appropriate tool based on the graph's properties (weighted/unweighted, presence of negative edges) and the specific problem requirements (single-source, all-pairs).
© 2025 ApX Machine Learning