Purpose: Find the shortest paths (loops allowed) from to in — asymptotically optimal, and vastly faster than Yen’s when is large.

The Idea

  1. Compute the shortest-path tree to (Dijkstra on the reversed graph), giving = distance from to .
  2. Define the sidetrack cost of an edge :

    Tree edges have . Any path is the shortest path plus a sequence of sidetrack edges, and its length is .
  3. So the problem becomes: enumerate sets of sidetrack edges in increasing total . This is a -smallest-sums problem.
  4. Eppstein builds a path graph : a heap-ordered DAG whose nodes are sidetrack edges, with out-degree 4 and where a path from the root corresponds to a valid sidetrack sequence. Running a -smallest search (a heap-based best-first traversal) on yields the answer in .

The heavy machinery is step 3-4: a persistent leftist/ randomized heap per vertex, where each vertex’s heap is its parent’s heap plus its own outgoing sidetracks. Persistence is what keeps the total size rather than .

Simplified practical version

Most contest problems accept a much simpler variant that skips the path-graph construction entirely:

// K shortest s->t walks, loops allowed. d[] = distance to t (reverse Dijkstra).
vector<long long> kShortest(int s, int t, int K) {
    vector<long long> res;
    vector<int> cnt(n, 0);
    priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
    pq.push({d[s], s});                       // A*-style: key = g + h
    while (!pq.empty() && (int)res.size() < K) {
        auto [cost, u] = pq.top(); pq.pop();
        if (cnt[u] >= K) continue;
        cnt[u]++;
        if (u == t) res.push_back(cost);
        for (auto [v, w] : adj[u])
            pq.push({cost - d[u] + w + d[v], v});
    }
    return res;
}

This is -times-A* with the consistent heuristic : it pops each vertex at most times, and the -th pop of is the -th shortest walk. Simple, correct, and fast enough for typical .

Complexity

VersionTimeLoops?
Full Eppstein (path graph)allowed
A*-with-counters (above)allowed
Yenforbidden

Paradigm

Reduction to a -smallest-sums problem over a heap-ordered structure, plus a potential function () that makes every edge non-negative — the same reweighting trick as Johnson’s algorithm.

Variants / Use Cases

  • When you need loopless paths — use Yen; Eppstein’s output may repeat vertices
  • K shortest paths in a DAG — trivial DP, no need for either algorithm
  • Route planning with alternatives, k-best parses / alignments, network resilience analysis
  • Second shortest path () — a single Dijkstra pair (forward and reverse) plus an edge scan is enough; don’t reach for Eppstein