Finding the shortest path between two points in a graph is a classic algorithms question with many good answers (Dijkstra's algorithm, Bellman-Ford, etc.) My question is whether there is an efficient algorithm that, given a directed, weighted graph, a pair of nodes s and t, and a value k, finds the kth-shortest path between s and t. In the event that there are multiple paths of the same length that all tie for the kth-shortest, it's fine for the algorithm to return any of them.
I suspect that this algorithm can probably be done in polynomial time, though I'm aware that there might be a reduction from the longest path problem that would make it NP-hard.
Does anyone know of such an algorithm, or of a reduction that show that it is NP-hard?
You're looking for Yen's algorithm for finding
K
shortest paths. Thek
th shortest path will then be the last path in that set.Here's an implementation of Yen's algorithm.
And here's the original paper describing it.
The best (and basically optimal) algorithm is due to Eppstein.
Even though, the question has a valid accepted answer, this answer addresses the problem of implementation by providing a sample working code. Find the valid code for kth shortest path here:
From the available Kth shortest path algorithms, Yen’s algorithm is the easiest to understand. Mostly this is because Yen’s algorithm first needs to compute all K-1 shortest paths before it can compute the Kth shortest path, so it can be broken down into sub-problems.
Furthermore, since each sub-problem uses a standard shortest path algorithm (e.g. Dijkstra’s algorithm), it is a more natural extension from the 1st shortest path problem to the Kth shortest path problem.
Here is how it works for finding the 3rd shortest path in an example graph with equal-weight edges.
1st shortest path (K=1)
If we are looking for the 1st shortest path between a start and a destination (here, between
D
andF
), we can just run Dijkstra’s algorithm. The entire code for Yen’s algorithm at the first iteration is:Given a starting graph, this gives the 1st shortest path (K=1).
2nd shortest path (K=2)
The intuition for finding the 2nd shortest path is to take the 1st shortest path but “force” Dijkstra’s algorithm along a different, slightly less optimal route. The way Yen’s algorithm “forces” Dijkstra’s algorithm along a different route, is by removing one of the edges that are part of the 1st shortest path.
But which of the edges do we remove to get the next shortest path? We need to try removing each edge, one by one, and see which edge removal gives us the next shortest path.
First we try to remove edge
D-E
(the first edge inshortest_1
), and then complete the shortest path by runningDijkstra(graph_1, D, F)
. We combine the shortest path already known from nodeD
toD
(which is just the nodeD
itself), with the new shortest path from nodeD
toF
. This gives us an alternative pathD->F
.Then we try to remove the edge
E-F
(the second edge inshortest_1
), and then complete the shortest path by runningDijkstra(graph_2, E, F)
. We combine the shortest path already known from nodeD
toE
, with the new shortest path from nodeE
toF
. This gives us yet another alternative pathD->F
.The procedure for the 2nd iteration thus looks like this:
At the end, the shortest of the alternative new paths is chosen as the 2nd shortest path.
3rd shortest path (K=3)
Just as the 2nd shortest path was found by removing edges from the 1st shortest path, the 3rd shortest path is found by removing edges from the 2nd shortest path.
The new nuance this time, however, is that for when we remove edge
D-A
(the first edge inshortest_2
), we also want to remove edgeD-E
. If we don’t do this, and remove only the edgeD-A
, then when we run Dijkstra ongraph_3
, we will find the 1st shortest path again (D-E-F
), instead of the 3rd shortest path!For
graph_4
andgraph_5
, however, we do not need to remove any other edges, since those edges, when used, won’t give us previously seen shortest paths.Thus, the overall procedure looks similar to finding the 2nd shortest path, but with the nuance that we also want to remove some edges seen in the 1st shortest path in addition to the 2nd shortest path. The decision is made based on whether
shortest_1
andshortest_2
share a subpath leading up to the edge which is being removed.Note how when we pick the shortest path this time, we take into account unused candidates from iteration 2 (i.e.
candidate_2
), and actually end up picking the shortest path found fromgraph_2
. In the same way, on the next iteration (K=4), we will find that the 4th shortest path was actually found in iteration K=3. As you can see, this algorithm is doing work in advance, so while it is finding the Kth shortest path, it is also exploring some of the paths beyond the Kth shortest path. It is thus not the most optimal algorithm for the Kth shortest path problem. The Eppstein algorithm can do better, but it is more complex.The above approach can be generalized by using several nested loops. The Wikipedia page on Yen’s algorithm already gives excellent pseudocode for the more generic implementation, so I will refrain from writing it here. Note that the Wikipedia code uses a list
A
to hold each shortest path, and a listB
to hold each candidate path, and that candidate shortest paths persist across loop iterations.Remarks
Due to the use of Dijkstra’s algorithm, Yen’s algorithm cannot have a Kth shortest path that contains a loop. This is not as noticable when un-weighted edges are used (as in the example above), but could occur if weights are added. For this reason, Eppstein’s algorithm works better as well, since it considers loops. This other answer includes a link to a good explanation of Eppstein’s algorithm.