Assignment 13 -- Due Monday, December 7
(at the beginning of lab)
CS 4521 Fall Semester, 2009
25 Points
Topics: Minimum Spanning Trees and Single Source Shortest Paths
The assignment
consists of two parts.
In the first part
you will hand-trace the algorithms of Kruskal and Prim to find
minimum spanning trees.
In the second part, you will implement Dijkstra's
Algorithm to solve the single-source shortest paths problem.
Part I: Minimum Spanning Tree Exercises (10 points)
Consider the following undirected graph:
5
(B)----------(D)
|\ / | \
| \10 20/ | \
| \ / | \
| \ / 12| |
3| \ / | |
| (A)----(F) |11
| 2 \ |
| 4\ |
| \|
(C)--------------(E)
15
- Trace the MST-Kruskal algorithm on this graph. Start by drawing the
vertices only:
(B) (D)
(A) (F)
(C) (E)
and show how the MST is grown by showing a snapshot of it after each edge is
added.
- Trace the MST-Prim algorithm on this graph starting with root
C
by showing the priority queue
after each iteration of the while loop. Also show the key
value for each node. The initial queue should look like:
0 inf inf inf inf inf
Q: ( C A B D E F )
Part II: Implementation of Dijkstra's Algorithm (15 points)
For this part, implement
Dijkstra's algorithm (page 595),
and run it on two graphs.
As discussed on page 599,
the min-priority queue used in Dijkstra's algorithm may be implemented in
four ways (each key value
key[u])
will always be equal to the distance estimate
d[u]):
- (15 points) an array implementation, in which the queue is
maintained by the
key[] array and an auxiliary array
inQ[] of booleans that indicates whether vertex
u is currently in the queue or not
(inQ[u] would be true or false respectively).
To initialize the queue (Line 3),
for u = 1 to n insert d[u]
into the priority queue.
I changed the while-loop to a for-loop and
iterated n times (though it is simple enough to keep count
of the number of items in the queue and quitting the while-loop when this
number drops to 0). Also, it is sufficient to iterate n-1
as indicated by Exercise 24.3-3 (whose short answer is "yes").
The initSingleSource() and relax()
functions (and dijkstra() for that matter) can be
member functions of the Graph class.
To keep d[v] and key[v] "synchronized",
Line 2 of relax() should involve two operations:
then (1) d[v] <- d[u] + w(u,v) and
(2) DecreaseKey( Q, v, d[u] + w(u,v) )
(remember that this is a decrease-key operation).
To implement extractMin(), just do a linear search
for the location of the minimum key[] value that is still
in inQ[].
Note that a vertex u is an array index and therefore
a handle into the priority queue,
and conversely an array index i of
key[] or inQ[] is a handle to vertex
i of the graph - moreover, neither of
these handles ever change.
- (17 points) the binary heap or (19 points) binomial heap
implementations.
In these implementations, the vertices and corresponding heap elements must
maintain
handles to each other (though, since the vertices don't change position,
the handle to a vertex (stored with its key) in the heap doesn't change
once it is set initially;
however the handles from the vertices into the heap will change --
they can be stored in an array
handleToHeap[], so handleToHeap[u]
points to the node in the heap with d's key value.
These handles would be integer indexes or pointers for binary heaps
or binomial heaps respectively.
- (25 points) the Fibonacci heap implementation. The main
problem here is to implement a Fibonacci heap. You have to have the
same kind of handles for the Fibonacci heap implementation as for the
binomial heap implementation. But there is a subtle difference: once
initialized, neither of the handles change, so this implementation is
somewhat easier (once you have a Fibonacci heap).
Discussion:
Use numbers 1, 2, 3, 4, 5 instead of the letters
s, t, x, y, z to identify the vertices.
Also, the set S
is only used to prove correctness of the
algorithm -- so you don't have to include code for it.
You can use a large number, say 1000
(I think 1 + the sum of the weights of all the edges is
enough), instead of infinity in the initialization
(I don't think there will be any arithmetic problems caused by using
1000 instead of INT_MAX or other fancy arithmetic).
Also, you can use 0 for NIL, the intialization value for the
pi field.
After initialization and at the end of each iteration of
the
while loop
of Dijkstra's algorithm,
your program should print out a list of all vertices,
their d-values
and their pi-values
(it might also be useful for
debugging to print out the priority queue).
(Note: instead of the
while loop,
you can use a for-loop from 1 to n, though it
would be easy to implement the empty() function for the priority
queue - just keep track of the number of elements with "true" values
in the inQ[] array.)
Run this implementation on four different (G,w,source)
combinations:
- The graph and weights G,w of Figure 24.2 page 585,
with source s ( = 1 ) - you may get one of the solutions
shown in (b) or (c) of Figure 24.2, or something different:
graph 24.2, source s.
This is essentially Exercise 24.3-1 on page 600.
- The graph and weights G,w of Figure 24.2 page 585,
but with source z ( = 5 ):
graph 24.2, source z.
- The graph and weights G,w of Figure 24.6 page 596,
with source s ( = 1 ) - you may very well get the solution
of Figure 24.6, or something different:
graph 24.6, source s.
- The graph and weights G,w of Figure 24.6 page 596,
but with source z ( = 5 ):
graph 24.6, source z.
Also
for each case, after the program run,
draw
the shortest-paths tree given by the predecessor graph.
The data files have the number of vertices, n, and
the source, source on the first line.
Each subsequent line contains three values
u
v, and
w,
representing an edge,
where the edge goes from vertex u to vertex v
and has weight w.
Here is what the output from the first test should look like:
Built graph, n = 5
Run Dijkstra with source = 1
Iteration 1, Distances, d[1] = 0, d[2] = 3, d[3] = 1000, d[4] = 5, d[5] = 1000
Iteration 2, Distances, d[1] = 0, d[2] = 3, d[3] = 9, d[4] = 5, d[5] = 1000
Iteration 3, Distances, d[1] = 0, d[2] = 3, d[3] = 9, d[4] = 5, d[5] = 11
Iteration 4, Distances, d[1] = 0, d[2] = 3, d[3] = 9, d[4] = 5, d[5] = 11
Iteration 5, Distances, d[1] = 0, d[2] = 3, d[3] = 9, d[4] = 5, d[5] = 11
Final distances and predecessors:
Distances, d[1] = 0, d[2] = 3, d[3] = 9, d[4] = 5, d[5] = 11
Predecessors, pi[1] = 0, pi[2] = 1, pi[3] = 2, pi[4] = 1, pi[5] = 4
This corresponds to Figure 24.2 (b) on page 585. It is possible that
changing the order of the adjacency lists could produce the shortest
paths tree of Figure 24.2 (c).
Figure 24.6 (page 596) shows how the third test run should proceed.
Helpful code:
Here is a .h file for a new Graph class
graph.h
and skeleton code for the Graph class implementation
graph.cpp
(you implement parts associated with Dijkstra's algorithm).
Note: the Graph class has been designed to work with both
weighted and unweighted graphs - weighted in this case.
Here is a .h file for the "array" priority queue class
PQueue
pqueue.h
and code for the PQueue class implementation
pqueue.cpp.
Here is a "driver" program that tests Dijkstra's algorithm:
driver.cpp.
Here is a "Makefile" that puts it all together:
Makefile.
When you download these files, be sure to remove the ".txt" suffixes
(by moving them to the same file name without the ".txt").
What to turn in:
- For Part 1, turn in your handwritten solutions to both parts.
- For Part 2, turn in:
- your (annotated) code,
- printouts from each of the four program runs, and
- diagrams of the final shortest-paths tree for each of those runs.
Page URL: http://www.d.umn.edu
/~ddunham/cs4521f09/assignments/a13/assignment.html
Page Author: Doug Dunham
Last Modified: Tuesday, 08-Dec-2009 10:25:37 CST
Comments to: ddunham@d.umn.edu