CS410, Summer 1998 Lecture 26 Outline Dan Grossman Goals: * Finish MST * SSSP Recall generic MST -- find a light edge for a cut that A respects and add it to A. Recall Kruskal Kruskal: sort edges by weight A empty initialize union-find with each vertex in its own set. for each edge (u,v) in order if (find(u) != find(v)) then add (u,v) to A and union(u,v) return A Another method (Prim's algorithm) just builds up one tree, always adding an edge to the tree that is the lightest edge between the tree and the rest of the graph. Prim: A empty S just has some r from V add to A a light edge for (S, V-S) and add the vertex to S How to really do it: think of adding one vertex each time -- the vertex which can most cheaply be added to S. So get-min from V-S with key being the distance (infinity if unreachable). That is, use a heap (at least conceptually). Prim: while heap not empty u = delete-min add (pred[u], u) to A for each edge (u,v) if v still in heap and weight(u,v) < v.key then set pred[v] to u and decrease-key(v, weight(u,v)) We will discuss the running time of Prim's algorithm next time when we also discuss the running time of Dijkstra's algorithm for single-source shortest path. Single-source Shortest Path Let G be a weighted, directed graph and s be a particular vertex in G. We want a least-weight path from s to every vertex in G. This is the single-source shortest path problem. Notice the problem is ill-defined if there are negative weight cycles in the graph. In class we will assume all weights are positive. The text has an algorithm that works even with negative weights. Note several similar problems: * single-destination shortest path: we can solve this by solving single-source shortest path on the transpose of G * signle-source single-destination shortest path: nobody knows how to solve this any faster than single-source shortest path in the worst case. * all pairs shortest path: tomorrow's lecture Lemma: Subpaths of shortest paths are shortest paths. Proof: Assume the shortest path from u to v has a subpath w_1 ---> w_2 If it does not take the shortest path from w_1 --> w_2, then by taking the shortest path we can make a shorter u --> v path. This contradicts the assumption that we have the shortest u --> v path. Definition: Let delta(v) be the total weight of the shortest path from u to v. Lemma: delta(v) <= delta(u) + weight((u,v)). Proof is as straightforward as the previous one. Recall that BFS solves our problem if every edge weight is 1. So we're really just generalizing the idea -- instead of pulling the next thing out of a queue, we will maintain every vertex's shortest _known_ path from s and decrease these known values as appropriate. We put the known values in a dist array and call the decreasing "relaxation". We also have a pred array as we did in BFS. relax((u,v)) : if dist[v] > dist[u] + weight((u,v)) then dist[v] = dist[u] + weight((u,v)) pred[v] = u Lemma: If we initialize dist[s] = 0 and every other dist[v] to infinity, then we can do any number of relaxations we want in any order and we will always have for all v that dist[v] >= delta[v]. Proof: By induction on the number of relaxations. Base: delta[s] = 0 and delta[v] <= infinity. Induction: Suppose we relax (u,v). If dist[v] <= dist[u] + weight((u,v)) then nothing happens so the claim must still be true. Else we assign dist[v] = dist[u] + weight((u,v)) Well dist[u] + weight((u,v)) >= delta[u] + weight((u,v)) by I.H. on u >= delta[v] by Lemma above So we can solve the problem by doing relaxations until every dist[v] equals delta[v]. By doing relaxations in an efficient order, we will get a good algorithm. Here is Dijkstra's algorithm: Dijkstra: initialize dist array as described in last lemma. while (more shortest paths to find) u = vertex with minimum distance value that hasn't already been extracted for each edge (u,v) relax((u,v)) We can use a heap just like we did with Prim's algorithm. We'll discuss running time more next lecture. Theorem: Dijkstra's algorithm is correct. We must show that when a vertex u has the smallest dist value of all those not already extracted (i.e. when u is extracted), delta[u] = dist[u]. This is sufficient since everything gets extracted eventually and dist values never increase. Proof: By induction on while loop iterations with inductive hypothesis: all vertices v already extracted had delta[v] = dist[v] when they were extracted. Base: Trivial. At the beginning, none have been extracted. Inductive: Suppose for contradiction we extract u but dist[u] > delta[u]. Then the current path to u (as maintained by the pred array) is not a shortest path. In the shortest path, there must be an edge x-->y where x has been extracted, y has not, and y != u. (s is extracted, u is not, so we must cross from extracted to not extracted somewhere. If y == u, then when x was extracted, delta[x] == dist[x] and we called relax((x,u)). So that would make dist[u] == delta[u] since this is the shortest path, but we are assuming dist[u] > delta[u]). Hence y was relaxed such that dist[y] == delta[y] when x was extracted (the shortest path to y is this subpath by the first lemma). So dist[y] == delta[y] by preceding argument < delta[u] by shortest path to u goes through y (*) <= dist[u] by lemma So we would have extracted y, not u. Contradiction. (*) This is where Dijkstra's algorithm correctness depends on positive edge weights!