Shortest Paths
Given a graph $G=(V, E)$ and a distance function $d:E \rightarrow \mathbb{N}$, we want to find the shortest path between two vertices $u, v \in V$. The shortest path $p \in E^*$ is a sequence of edges such that $u \overset{p}{\leadsto} v$ and $\sum_{e \in p}{d(e)}$ is minimal.
The algorithms below use adjacency lists to represent the graph $G=(V, E)$. Each vertex $v \in V$ contains a list of edges $E(v) \subseteq E$ that connect it to another vertex $u \in V$. We will denote this set of connected vertices as $V(v) \subseteq V$.
#define MAX_VERTICES 1000
#define MAX_EDGES (MAX_VERTICES) /* edges per vertex */
typedef struct {
int edge[MAX_VERTICES][MAX_EDGES];
int distance[MAX_VERTICES][MAX_EDGES];
int num_edges[MAX_VERTICES];
int num_vertices;
} graph_t;
For non-negative distances
Find the shortest path between two vertices of a graph where the distances between two vertices are non-negative.
Input A graph $G=(V,E)$ with distance function $d:E \rightarrow \mathbb{N}$,
and two vertices $u, v \in V$
Output The shortest path from $u$ to $v$
Time $O(|V|^2)$
This algorithm is attributed to Dijkstra. Initially, we set the shortest distance $\delta$ from $u$ to all other vertices as infinite. We then iterate over the non visited vertices, starting with $u$. Let the current node be $v$. We mark $v$ as visited and update the shortest distance of all its connected vertices $x \in V(v)$, setting $\delta(x)$ as $\delta(v) + d(v, x)$ if the new distance is shorter. We repeat this process by selecting the next non visited vertex with shortest distance $\delta$. Eventually we will reach the destination vertex (if it is connected), and we can reconstruct the shortest path using the edges we select during the distance updates.
#define INFINITY (MAXINT >> 1)
typedef struct {
...
char visited[MAX_VERTICES]; /* either 0 or 1 */
int shortest[MAX_VERTICES];
int prior[MAX_VERTICES]; /* connecting vertex in shortest path */
} graph_t;
int shortest_path(graph_t *graph, int source, int destiny) {
int v, e, current;
for (v = 0; v < graph->num_vertices; v++) {
graph->visited[v] = 0;
graph->shortest[v] = v == source ? 0 : INFINITY;
}
current = source;
while (current != destiny && current != -1) {
graph->visited[current] = 1;
if (current == destiny) break;
for (e = 0; e < graph->num_edges[current]; e++) {
int next = graph->edge[e];
if (graph->visited[next]) continue;
int new_distance = graph->shortest[current]
+ graph->distance[current][e];
if (new_distance < graph->shortest[next]) {
graph->shortest[next] = new_distance;
graph->prior[next] = current;
}
}
for (current = -1, v = 0; v < graph->vertices; v++) {
if (!graph->visited[v] && (current == -1
|| graph->shortest[i] < graph->shortest[current])) {
current = v;
}
}
}
if (current == -1) return INFINITY;
return graph->shortest[destiny];
}
We can easily extend this algorithm to compute the shortest path from $u$ to all
other vertices. We simply repeat the while
loop until all reachable edges have
been visited. The algorithm still runs in $O(|V|^2)$.
For negative distances
Find the shortest path between two vertices of a graph where the distances between two vertices may be negative.
Input A graph $G=(V,E)$ with distance function $d: E \rightarrow \mathbb{Z}$,
and two vertices $u, v \in V$
Output The shortest path from $u$ to any other vertex
Time $O(|V| \cdot |E|)$
Dijkstra’s algorithm does not work when distances are negative, so the problem requires a different approach. The algorithm below is attributed to Bellman & Ford. We start by setting the shortest distances $\delta$ from $u$ to all other vertices as infinite. Then, for each edge $(i, j) \in E$, we relax the shortest distance $\delta(j)$ by setting it as $\delta(i) + d(i, j)$, if it is shorter. We apply this relaxation $|V| - 1$ times, which will compute the shortest path from $u$ to any other vertex. However, if there is a cycle in the graph with negative distance, the shortest path cannot be computed, since we can loop indefinitely in the cycle to reduce the distance.
To detect if such a cycle exists, we execute one more relaxation on the shortest distances. If it would reduce any distance even further, then a negative cycle must exist. Bellman-Ford’s algorithm is frequently used to detect this type of cycle.
#define INFINITY (MAXINT >> 1)
typedef struct {
...
int shortest[MAX_VERTICES];
int prior[MAX_VERTICES]; /* connecting vertex in shortest path */
} graph_t;
int shortest_path(graph_t *graph, int source, int destiny) {
int i, j, k, e, new_distance;
for (i = 0; i < graph->num_vertices; i++) {
graph->shortest[i] = i == source ? 0 : INFINITY;
}
for (k = 0; k < graph->num_vertices; k++) {
for (i = 0; i < graph->num_vertices; i++) {
for (e = 0; e < graph->num_edges[i]; e++) {
j = graph->edge[i][e];
new_distance = graph->shortest[i] + graph->distance[i][e];
if (new_distance < graph->shortest[j]) {
/* The last loop detects negative cycle */
if (k == graph->num_vertices - 1) return -INFINITY;
graph->shortest[j] = new_distance;
graph->prior[j] = i;
}
}
}
}
return graph->shortest[destiny];
}
For all paths
Find the distance of the shortest path between all vertices. Distances can be negative, but the graph has no negative cycle.
Input A graph $G=(V,E)$ with distance function $
d:E \rightarrow \mathbb{Z}$
Output The distance of the shortest path between all vertices
Time $O(|V|^3)$
This algorithm is attributed to Floyd & Marshall. We use a strategy similar to Bellman-Ford’s. Let $\delta:V^2 \rightarrow \mathbb{Z}$ be the shortest distance between two vertices. Initially, $\delta(i, j)$ equals to $d(i,j)$ if vertices $i, j \in V$ are connected, or infinite otherwise. Then, for each vertex $k \in V$, we relax the distance for each pair $i, j \in V$ using the distance $\delta(i, k) + \delta(k, j)$ if it is shorter than $\delta(i, j)$. It will eventually produce the shortest distances for each pair.
Notice that if the distances were non-negative we could run Dijkstra’s to obtain all shortest paths, executing it $|V|$ times. It would have the same time complexity $O(|V|^3)$.
#define INFINITY (MAXINT >> 1)
typedef struct {
...
int shortest[MAX_VERTICES][MAX_VERTICES];
} graph_t;
void all_shortest_paths(graph_t *graph) {
int k, i, j, e;
for (i = 0; i < graph->num_vertices; i++) {
for (j = 0; j < graph->num_vertices; j++) {
graph->shortest[i][j] = i == j ? 0 : INFINITY;
}
for (e = 0; e < graph->num_edges[i]; e++) {
graph->shortest[i][graph->edge[e]] = graph->distance[i][e];
}
}
for (k = 0; k < graph->num_vertices; k++) {
for (i = 0; i < graph->num_vertices; i++) {
for (j = 0; j < graph->num_vertices; j++) {
int new_distance = graph->shortest[i][k] +
graph->shortest[k][j];
if (graph->shortest[i][j] > new_distance) {
graph->shortest[i][j] = new_distance;
}
}
}
}
}
Graphs |
---|
Graph Representation | Graph Traversal | Connections | Shortest Paths | Minimal Spanning Trees | Network Flow |