You are on page 1of 3

48

L ECTURE 24
Shortest-path problems: review: Single-source all destinations Dijkstras algorithm First, all edge-costs equal :Breadth First Search All pairs later Pseudo-code for each vertex v{ d[v] = infinity; // length of shortest path known thus far p[v] = s; // last vertex on this shortest path known[v] = false; // set to true when we KNOW shortest path has been discovered... } d[s] = 0; //s is the source vertex for (int i=0; i < |V|; i++){ //Each iteration, will find the TRUE shortest path to some vertex v = vertex u with smallest d[u] and (known[u] == false); known[v] = true; //we will NOT be able to find a shorter path to this v for each vertex w adjacent to v{ if (d[w] > d[v] + 1){ d[w] = d[v] + 1; p[w] = v; } } Run-time complexity (assuming adjacency list representation): Finding the vertex v: O(|V |) each iteration of the outer for-loop The inner for-loop: O(|E|) across all iterations of the outer for-loop For an overall run-time equal to O(|V |2 ) + O(|E|), which is O(|V |2 ). To reduce complexity Maintain a queue of vertices5 , such that the next vertex we seek (in the inner for-loop) is at the head of this queue
for each vertex v{ d[v] = infinity; // length of shortest path known thus far p[v] = s; // last vertex on this shortest path known[v] = false; // set to true when we KNOW shortest path has been discovered... } d[s] = 0; //s is the source vertex Q.enqueue(s); //added to the previous version for (int i=0; i < |V|; i++){ //vertex u with smallest d[u] and (known[u] == false); -- replaced by the next line v = Q.front(); Q.dequeue(); known[v] = true; for each vertex w adjacent to v{ if (d[w] > d[v] + 1){ d[w] = d[v] + 1; p[w] = v; Q.enqueue(w);//added to the previous version } }

Run-time complexity: Finding the vertex v: O(1) each iteration of the outer for-loop The inner for-loop: O(|E|) across all iterations of the outer for-loop For an overall run-time equal to O(|V | + |E|).
5. Would it be OK if we used a stack here (as we could for topsort)??

49

When edges have costs: Dijkstras algorithm BFS would not work on the graph {v1 , v2 , v3 , v4 }, {(v1 , v2 ), (v1 , v3 ), (v3 , v2 ), (v2 , v4 )} , and costs c(v1 , v2 ) = 5, c(v1 , v3 ) = c(v3 , v2 ) = c(v2 , v4 ) = 1

with source v1 : shortest path to v4 is incorrectly chosen to be v1 v2 v4 of cost 6, instead of v1 v3 v2 v4 of cost 3 Solution (assuming no negative costs!! Dijkstras algorithm does not work with negative costs): Use a priority queue: v = PQ.deleteMin(); No negative edges means: No future path of lower cost to v will ever be found (This would not work if there were negative edges in the DAG!!) In the above example: multiple entries of v2 Therefore, need to replace Q.enqueue(w) by: if w is in PQ then PQ.delete(w) Need an enhanced implementation of priority queues: allows for determining whether an element is in the PQ, and deleting, both in O(log n) time.6 will revisit this point later see not really necessary
for each vertex v{ d[v] = infinity; p[v] = s; known[v] = false; } d[s] = 0; PQ.insert(s); for (int i=0; i < |V|; i++){ v = PQ.deleteMin(); known[v] = true; for each vertex w adjacent to v{ if (d[w] > d[v] + c(v,w)){ d[w] = d[v] + c(v,w); p[w] = v; if (w is in PQ) PQ.delete(w) PQ.insert(w); //The above two lines can also be implemented using the decreaseKey operation on //(enhanced) priority queues } }

Run-time complexity: Initialization: O(|V |) The deletemins: O(log |V |) per iteration The inner loop: O(log |V |) per edge, which yields O(|E| log |V |) across all iterations Yielding a run-time complexity expression of O(|V |)+O(|V | log |V |)+O(|E| log |V |), which is usually dominated by the O(|E| log |V|) term Another example: Figure 9.20 (p 374) of the text

6. Read Section 6.3.4 Other Heap Operations of your text for how to get such an implementation. Basically, you need an O(1)-nd hash table (e.g., cuckoo hashing) in conjunction with the additional delete priority-queue operation.

50

Implementation simplication: Dont really need the delete for each vertex v{ d[v] = infinity; p[v] = s; known[v] = false; } d[s] = 0; PQ.insert(s); for (int i=0; i < |V|; i++){ v = PQ.deleteMin(); while (known[v]) v = PQ.deleteMin(); known[v] = true; for each vertex w adjacent to v{ if (d[w] > d[v] + c(v,w)){ d[w] = d[v] + c(v,w); p[w] = v; PQ.insert(w); } } Run-time complexity: Initialization: O(|V |) The priority queue may now be larger a vertex may appear multiple times. A safe upper bound on the priority-queue size is |E|: each edge may have caused a vertex to be inserted. The deletemins: O(log |E|) per edge!! since each edge may have caused a vertex to be inserted into the priority queue. So, O(|E| log |E|) across all iterations The inner loop: O(log |E|) per edge, which yields O(|E| log |E|) across all iterations. Yielding a run-time complexity expression of O(|V |)+O(|E| log |E|)+O(|E| log |E|), which is usually dominated by the O(|E| log |E|) term. Noting that log |E| log |V |2 = 2 log |V | = O(log |V |), we conclude that the asymptotic complexity remains O(|E| log |V|)

You might also like