Proofs: Contraction hierarchies
These are the human proofs of Contraction hierarchies, at full length.
Contraction hierarchy queries return shortest paths full
This proof shows: correctness, termination.
Claim. Let all weights be nonnegative and let the hierarchy be built by lines L1 to L5 with any order. For all , the query returns , and when it is finite, a walk in from to of that weight.
Setup. Let be the position of in the order. Let be the remaining graph just before is contracted: its vertices are those of rank at least , and its edges are the original edges among them and the shortcuts added so far, each pair keeping its least weight. Let be the graph of all edges that are ever stored as upward or downward edges in line L5, with their stored weights. An edge between and can only be added or lowered while both are uncontracted, and it is stored, with its final weight, when the first of them is contracted; so each edge of is stored once, and for of lower rank than and , the edges and of have the same weights in .
Step 1: has the distances of . By induction on the contractions, every edge of every is an edge of or a shortcut of weight for edges of an earlier remaining graph, so each edge of has the weight of a walk in between its ends. Every edge of that is not a self-loop is in for the lower end with at most its weight, so it is in with at most its weight. Hence , and expanding an -walk edge by edge (each shortcut through its middle, recursively, which ends since middles have lower rank than both ends) gives a -walk of the same weight.
Step 2: a shortest up-down walk exists. Call an - walk in up-down if no inner vertex has lower rank than both its neighbors on the walk; then the ranks rise to a top vertex and then fall. Suppose . For a walk let count, for each rank , the inner vertices of of rank , and order these vectors lexicographically starting from rank 0; this order on is a well-order. Among shortest - walks in choose with the least . If is not up-down, it has an inner vertex with neighbors on of higher rank, edges . Then , since otherwise is a closed walk that can be cut out, giving a shortest walk with smaller . When was contracted, and were in with these edges. By line L3, either without had a - walk of weight at most , all of whose vertices have rank above , or line L4 put an edge of weight at most into for the next vertex , which takes the role of . Each edge of lies in a later remaining graph, so it is in with weight at most its weight in . Replacing by gives an - walk of weight at most , hence shortest, in which one inner occurrence of rank is removed and only inner vertices of higher rank are added. Its count vector is lexicographically smaller, a contradiction. So is up-down.
Step 3: the two searches compute upward distances. The forward search is Dijkstra's algorithm from on the upward edges, and the backward search is Dijkstra's algorithm from on the reversed downward edges, both with nonnegative weights. Let be the least weight of an - walk in whose ranks increase, and the least weight of a - walk whose ranks decrease. Every edge of joins vertices of different rank and is stored as an upward edge out of its lower end or as a downward edge into its lower end, so these are exactly the graphs the two searches use. When a side scans , its label is (or ), by the correctness of Dijkstra; and every tentative label is the weight of a real walk.
Step 4: the query is correct. Every value given to in line L8 is a scanned label plus a tentative or final label of the other side, the weight of an - walk in , so always. If , no such walk exists, line L8 never remembers a vertex, and line L9 returns . Otherwise let be a shortest up-down walk (Step 2) with top vertex ; its two halves give . Suppose the query stops with . Then both least keys were at least and , and Dijkstra scans in nondecreasing key order, so both sides had already scanned with its final labels. At the later of the two scans, line L8 set , and never increases, a contradiction. So . The remembered vertex has labels whose walks, read through the predecessors, form an - walk in of weight (labels only fall, and the predecessor chain of the last setting has exactly that weight), and by Step 1 its expansion is a walk in of the same weight.
Termination. The contraction loop handles each vertex once and each witness search is a Dijkstra search on a finite graph. Each query side scans each vertex at most once, the loop stops when both heaps are empty or both least keys reach , and the expansion ends by Step 1.