input: graph/digraph, non-negative edge/arc weights, start node s
output: for each node v, shortest path from s to v
includes proof of correctness
dist parent fringe pick A B C D E A B C D E - 0 - - - - A - - - - A initialize A min-dist fringe node 0 4 2 - - A A A - - BC update nbrs C min-dist fringe node 0 3 2 6 7 A C A C C BDE update nbrs B min-dist fringe node 0 3 2 5 6 A C A B B DE update nbrs D min-dist fringe node 0 3 2 5 6 A C A B B E update nbrs E min-dist fringe node 0 3 2 5 6 A C A B B E update nbrs
dist[s] == 0 fringe = { s } while not isempty(fringe): v = a fringe vertex with min dist remove v from fringe for each nbr w of v: add w to fringe (if not there already) newdist = dist[v] + wt(v,w) if newdist < dist[w]: dist[w] = newdist parent[w] = v
def weightSum(G): sum = 0 for u in G: for (v,weight) in G[u]: sum += weight return sum def showAll(G,D,P): print '\nprnt', for v in sorted(G): print P[v], print '\nnode', for v in sorted(G): print v, print '\ndist', for v in sorted(G): print D[v], print '' def showFringe(G,F,D,inf): print ' fringe', for v in sorted(F): if D[v]!=inf: print v,D[v],' ', def sssp(G,start): infinity = 999 + weightSum(G) dist, parent, fringe = {}, {}, [] for v in G: dist[v], parent[v] = infinity, -1 fringe.append(v) dist[start],parent[start] = 0, start doneEarly = False while len(fringe)>0 and not doneEarly: showFringe(G,fringe,dist,infinity) ndxMin = 0 for j in range(1,len(fringe)): if dist[fringe[j]] < dist[fringe[ndxMin]]: ndxMin = j u = fringe.pop(ndxMin) print '\npick',u,':', if (dist[u] == infinity): doneEarly = True for (v,duv) in G[u]: if dist[v] > dist[u] + duv: dist[v] = dist[u] + duv parent[v] = u print v,dist[v],' ', print '' showAll(G,dist,parent) G = {'A': [['B',4],['C',2]], 'B': [['C',3],['D',2],['E',3]], 'C': [['B',1],['D',4],['E',5]], 'D': [], 'E': [['D',1]]} sssp(G,'A')
n nodes removed from fringe
updating distance values when node v removed from fringe takes Θ(degree v) time
each vertex removed from fringe once, so total Θ(m) time
finding node to remove from fringe takes Θ(k) time, where k is number of nodes in fringe, so total &Theta(n2) time
so total Θ(m + n2) = Θ(n2) time
yes, if we can use priority queue: allows updates and deletemins in o(n2) time
first improvement: implement PQ with binary minheap
better: implement PQ with Fibonacci heap (not in this course)
n nodes, each with key, stored in array H[0 ... n-1]
array represents a complete binary tree
for node H[j]
parent H[ floor( (j-1)/2 ) ]
left child H[ 2*j +1 ]
right child H[ 2*j +2 ]
min-heap property
for each node v with a parent, key(parent(v)) <= key(v)
warning: text uses array indices
H[] is minheap, n is number items in H trickledown(H,n,j): while haschild(j) and out-of-order(j, minchild(j)): newj <- index of smaller child swap(node(j), node(newj)) j <- newj bubbleup(H,n,j): while hasparent(j) and out-of-order(parent(j), j): newj <- index of parent swap(node(newj), node(j)) j <- newj insert(H,n,x): n <- n+1 H[n] <- x bubbleup(n) decreasekey(H,n,j, x): H[j] <- x bubbleup(j) deletemin(H,n): m <- H[0] H[0] <- H[n-1] n <- n-1 trickledown(H,n,0) buildheap(H,n): for j in range(n): H[j] <- new node bubbleup(j) buildheapfast(H,n): for j in range(n): H[j] <- new node for j in range(0, (n-2)/2, -1): # descending order trickledown(j)
for j: 1 .. n-1: bubbleup(j) array: 4 7 2 1 6 9 5 3 0 8 compact binary tree: 4 7 2 1 6 9 5 3 0 8 bubbleup(1) 4 7 * * * * * * * * bubbleup(2) 2 * 4 * * * * * * * bubbleup(3) 1 2 * 7 * * * * * * bubbleup(4) * 2 * * 6 * * * * * bubbleup(5) * * 4 * * 9 * * * * bubbleup(6) * * 4 * * * 5 * * * bubbleup(7) * * * 3 * * * 7 * * bubbleup(8) 0 1 * 2 * * * * 3 * bubbleup(9) * * * * 6 * * * * 8 minheap 0 1 4 2 6 9 5 7 3 8 array: 0 1 4 2 6 9 5 7 3 8
for j: n/2 .. 0: trickledown(j) start: 4 7 2 1 6 9 5 3 0 8 trickledown(4) * * * * 6 * * * * 8 trickledown(3) * * * 0 * * * 3 1 * trickledown(2) * * 2 * * 9 5 * * * trickledown(1) * 0 * 1 6 * * 3 7 * trickledown(0) 0 1 2 3 6 * * 4 7 * minheap 0 1 2 3 6 9 5 4 7 8 array: 0 1 2 3 6 9 5 4 7 8
* 1 1 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
bup(1) bup(2) bup(3) … bup(n-1)
worst case: each bup goes to root
wc kc
Σ(j: 2 to n) ⌊ lg j ⌋
∈ Θ(n log n)
(wc kc is 2 times these values) 4 3 3 2 2 2 2 1 1 1 1 1 1 1 1 * * * * * * * * * * * * * * * *
n=3 1 = 3-2 n=7 1+1 +2 = 7-3 n=15 1+1+1+1 +2+2 + 3 = 15-4 n=31 1+1+1+1+1+1+1+1 +2+2+2+2 + 3+3 + 4 = 31-5 n=2^t-1 2^{t-1} + 2(2^{t-2}) + 3(2^{t-3}) + ... + (t-2)(2^1) + (t-1)2^0
sum 1*8 + 2*4 + 3*2 + 4*1 represented by 8A, 8B, 6C, 4D. notice sum is 16+15-5 = 26 * D * C C D * B B B B C C D * A A A A A A A A B B B B C C D *
td(n/2) td(n/2 -1) td(n/2 -3) … td(0)
worst case: each trickledown goes to leaf
simplifying assumption: n = 2k-1
wc kc
2 * ( 1*2k-2 + 2*2k-3 + 3*2k-4 + ... + (k-1)*2k-k )
2k+1 Σ(j: 1 to k-1) j / 2j+1
2k+1 Σ(j: 1 to k-1) j / 2j+1
2k+1 (1 - (k+1)/2k ) = 2(2k - (k+1))
∈ Θ(n)
claim: after bup(j), array[0 1 2 .. j] is a minheap
argue by induction
assume after bup(j-1), array]1 … j-1] is a minheap
now we call bup(j)
case: key[ parent(j) ] ≤ key[ j ]
now every node satisfies the heap property, we are done
case: key[ parent(j) ] > key[ j ]
now we swap these two, and since before the swap key[ parent(j) ] was no greater than the key of any other child, and we have made it smaller, it is still no greater than the key of any other child. continue arguing in this fashion if we bubbleup from parent(j).
2 5 8 8 7 ? * * * * * * * * *
trickledown, bubbleup, insert, decreasekey WC Θ(log n)
buildheap WC Θ(n log n)
buildheapfast WC Θ(n)
buildheapfast Θ(n)
n extractmins: Θ(n log n)
m decreasekeys: Θ(m log n)
total Θ((m+n) log n) = Θ(m log n)
with Fibonacci heap, Θ(m + n log n)
if neg wt edges and reachable neg wt cycle, then no sssp solution
if neg wt edges but no neg wt cycles, Bellman-Ford solves sssp
def infinity(): return 999 def myprint(d): if d==infinity(): print '--', else: print '%2s' % d, def update(x,y,dxy,D,P): change = 0 if D[x]<infinity() and D[y] > D[x] + dxy: D[y], P[y], change = D[x] + dxy, x, 1 return change def sssp(G,source): dist, parent = {}, {} for v in G: dist[v], parent[v] = infinity(), -1 dist[source],parent[source] = 0, source print 'from',source,' ', for v in sorted(G): print '%2s' % v, print '\ndistance ', for v in sorted(G): myprint(dist[v]) for _ in range(len(G)-1): updates = 0 for v in G: for (w,dvw) in G[v]: updates += update(v,w,dvw,dist,parent) print '\ndistance ', for v in sorted(G): myprint(dist[v]) if updates==0: break print '\nparent ', for v in sorted(G): print '%2s' % parent[v], print '' G = {'A': [['E',2]], 'B': [['A',1],['C',1]], 'C': [['D',3]], 'D': [['E',-1]], 'E': [['B',-2]], 'F': [['A',-4],['E',-1]], 'G': [['F',1]], 'S': [['A',10],['G',8]]} sssp(G,'S')