Algorithm Search⌘K

Proofs: Bidirectional Dijkstra

These are the human proofs of Bidirectional Dijkstra, at full length.

Bidirectional Dijkstra returns a shortest path full

This proof shows: correctness, termination.

Claim. Let GG be a directed graph with weights w≥0w \ge 0 and let s,ts, t be vertices. BIDIRECTIONAL-DIJKSTRA returns μ=δ(s,t)\mu = \delta(s, t). If μ<∞\mu < \infty, the returned vertex sequence is a walk from ss to tt of weight μ\mu, and it is a path when all weights are positive.

Termination. Each side inserts an entry into its queue only when line L9 lowers a label, which happens at most once per scan of an edge, and each vertex is marked done at most once per side. So each queue receives at most 1+m1 + m entries, each pass of the loop removes one entry, and the loop ends after at most 2(m+1)2(m + 1) passes, or earlier by line L4. When both queues are empty, line L4 sees ∞+∞≥μ\infty + \infty \ge \mu and stops.

Notation. The forward search runs on GG from ss; its distances are δf(v)=δ(s,v)\delta_f(v) = \delta(s, v). The backward search runs on the reversed graph from tt; its distances are δb(v)=δ(v,t)\delta_b(v) = \delta(v, t). Write min⁡(Q)\min(Q) for the least key in QQ, with min⁡(∅)=∞\min(\emptyset) = \infty.

Step 1: facts about each side. Each side, on its own, is Dijkstra's algorithm with a lazy-deletion heap; the other side only reads its labels in line L10. So the following hold for each side XX, from the proof of Dijkstra's algorithm:

  • (a) dX[v]≥δX(v)d_X[v] \ge \delta_X(v) always, and if dX[v]<∞d_X[v] < \infty, following πX\pi_X from vv gives a path to the root of side XX of weight exactly dX[v]d_X[v] (forward: from ss to vv; backward: from vv to tt).
  • (b) When uu is marked done on side XX, dX[u]=δX(u)d_X[u] = \delta_X(u), and the label never changes after that.
  • (c) At any moment, every vertex vv with δX(v)<min⁡(QX)\delta_X(v) < \min(Q_X) is done on side XX.

For (c): let vv be a vertex with δX(v)<∞\delta_X(v) < \infty not done on side XX, and let RR be a shortest path to vv on side XX. Its first vertex is done or is the root with its entry still in QXQ_X. Let yy be the first vertex of RR not done; if yy is the root, its entry (0,y)(0, y) is in the queue; otherwise its predecessor on RR is done, so by (b) and the relaxation of that edge, dX[y]=δX(y)d_X[y] = \delta_X(y) and the entry (δX(y),y)(\delta_X(y), y) is in QXQ_X. So min⁡(QX)≤δX(y)≤δX(v)\min(Q_X) \le \delta_X(y) \le \delta_X(v), since weights are nonnegative. This is the contrapositive of (c).

Step 2: μ\mu is an upper bound. When line L10 sets μ\mu from an edge (u,v)(u, v) scanned on the forward side, df[u]d_f[u] is the weight of a path s⇝us \leadsto u and db[v]<∞d_b[v] < \infty is the weight of a path v⇝tv \leadsto t by (a), so μ\mu is the weight of the walk s⇝u→v⇝ts \leadsto u \to v \leadsto t. The backward case is symmetric, with the edge used in its original direction. So μ≥δ(s,t)\mu \ge \delta(s, t) at all times, and if s=ts = t, μ=0=δ(s,t)\mu = 0 = \delta(s, t) from line L2.

Step 3: at the stop, μ≤δ(s,t)\mu \le \delta(s, t). Suppose s≠ts \ne t, the loop stops, and δ(s,t)<μ\delta(s, t) < \mu. Then δ(s,t)<∞\delta(s, t) < \infty; let P=(v0=s,v1,…,vk=t)P = (v_0 = s, v_1, \ldots, v_k = t) be a shortest path. Let F=min⁡(Qf)F = \min(Q_f) and B=min⁡(Qb)B = \min(Q_b) at the stop, so F+B≥μF + B \ge \mu. For each ii, δ(s,vi)+δ(vi,t)=δ(s,t)<F+B\delta(s, v_i) + \delta(v_i, t) = \delta(s, t) < F + B, so δ(s,vi)<F\delta(s, v_i) < F or δ(vi,t)<B\delta(v_i, t) < B.

Call viv_i forward if δ(s,vi)<F\delta(s, v_i) < F. Since δ(s,vi)\delta(s, v_i) does not decrease along PP, the forward vertices are v0,…,vjv_0, \ldots, v_{j} for some j∈{−1,0,…,k}j \in \{-1, 0, \ldots, k\} (j=−1j = -1 meaning none), and the others satisfy δ(vi,t)<B\delta(v_i, t) < B. By (c), forward vertices are done forward and the others are done backward.

  • If 0≤j<k0 \le j < k, the edge (vj,vj+1)(v_j, v_{j+1}) joins a vertex done forward to a vertex done backward.
  • If j=−1j = -1, all of PP is done backward. Take the edge (v0,v1)(v_0, v_1); its first end is ss with df[s]=0=δ(s,s)d_f[s] = 0 = \delta(s, s) from line L1 on.
  • If j=kj = k, all of PP is done forward. Take the edge (vk−1,vk)(v_{k-1}, v_k); its last end is tt with db[t]=0d_b[t] = 0.

In each case let (x,y)(x, y) be that edge. Consider the moment when the later of "x done forward" and "y done backward" happened; in the cases j=−1j = -1 and j=kj = k, consider when yy was done backward or xx was done forward, respectively. If that moment was the forward scan of xx, line L10 examined (x,y)(x, y) with df[x]=δ(s,x)d_f[x] = \delta(s, x) and db[y]=δ(y,t)d_b[y] = \delta(y, t) (final by (b), or 0 at tt). If it was the backward scan of yy, line L10 examined the reversed edge with db[y]=δ(y,t)d_b[y] = \delta(y, t) and df[x]=δ(s,x)d_f[x] = \delta(s, x). Either way, afterwards μ≤δ(s,x)+w(x,y)+δ(y,t)=w(P)=δ(s,t)\mu \le \delta(s, x) + w(x, y) + \delta(y, t) = w(P) = \delta(s, t), since PP is a shortest path and its sub-paths are shortest. As μ\mu never increases, this contradicts δ(s,t)<μ\delta(s, t) < \mu. Here x≠yx \ne y, since PP is a path and s≠ts \ne t, so line L10, which skips only self-loops, does examine (x,y)(x, y).

So μ=δ(s,t)\mu = \delta(s, t) by Steps 2 and 3. In particular, μ=∞\mu = \infty exactly when tt is not reachable from ss.

Step 4: the path. If μ<∞\mu < \infty and s≠ts \ne t, let (a,b)(a, b) be the edge remembered when μ\mu took its final value, scanned on side XX. At that time μ=df[a]+w(a,b)+db[b]\mu = d_f[a] + w(a, b) + d_b[b]. Labels only decrease, so at the end df[a]+w(a,b)+db[b]≤μd_f[a] + w(a, b) + d_b[b] \le \mu. By (a), πf\pi_f gives a path s⇝as \leadsto a of weight df[a]d_f[a] and πb\pi_b gives a path b⇝tb \leadsto t of weight db[b]d_b[b]. The joined walk has weight at most μ=δ(s,t)\mu = \delta(s, t), hence exactly δ(s,t)\delta(s, t). Each chain is a path. If the two chains share a vertex zz, the walk contains the closed walk z⇝a→b⇝zz \leadsto a \to b \leadsto z; its weight is 0, because cutting it would leave an ss-tt walk lighter than δ(s,t)\delta(s, t) otherwise. With all weights positive no closed walk has weight 0, so the chains are disjoint and the sequence is a path. ■\blacksquare

Back to Bidirectional Dijkstra