CS302 --- Weighted Graphs

Brad Vander Zanden

Weighted Graphs


I. Shortest Path Problem: Find the path in a weighted graph that (a) connects
	two vertices x and y, such that (b) the sum of the
	weights of all the edges in the path is minimized over all such paths.

    A. Important Insight: A greedy algorithm will work. A greedy algorithm
       tries to solve a problem in stages by doing what appears to be the
       best thing at each stage. In this case the greedy approach is to
       select an unknown vertex, v, at each stage which has the shortest
       path among all remaining unknown vertices. It can be proven that
       this path is indeed the shortest path to v. 

    B. Converting the insight to an algorithm: We want to do some type of 
        graph search. We will call the fringe the set of vertices that
	are not yet marked known but which have been seen at least once
	during the search. The next vertex to choose must come from this
	fringe set, since the paths to all unseen vertices are not yet
	known and hence these vertices should not be considered. We can
	write this insight as follows:

	Insight: If our search always selects the fringe vertex that has
		the shortest path from x, then when we visit y, we will
		have found the shortest path from x to y.

    C. Nailing down the algorithm: We want to perform a priority first
	search that starts from x. The priority of a fringe vertex is
	computed as the length of the currently known shortest path from
	x to that vertex (i.e., at each step we visit the vertex on the
	fringe that is closest to x). 

	1. Maintain a dist field with each vertex that keeps track of the 
	        currently known	shortest path from x to that vertex

	2. At each step visit the vertex with the minimum dist value

	3. Each time a vertex is visited (i.e., becomes a known vertex), 
		recompute the shortest paths for every unknown vertex 
		adjacent to the visited vertex. If vertex k is visited
		and vertex m is an unknown vertex adjacent to vertex k, the 
		computation is:

		m.dist = min(m.dist, k.dist + edge_weight(k,m))

    D. Time Complexity:

        1. In a sparse graph, priority-first search can compute the shortest 
	   path from x to any vertex in O((E lg V) time.

	2. In a dense graph, priority-first search can compute the shortest 
	   path from x to any vertex in O(V2) time.

    E. Code--see pp. 344-345 of Weiss

    F. Code Notes

       1. This algorithm is called Dijkstra's algorithm in honor of its
	  creator.

       2. If the graph is sparse, you should use a priority queue to
          find the next vertex to mark known

	  a. The decrease function in Fig 9.32 would add a vertex to the
	     priority queue if it was previously unseen (i.e., its 
	     dist field is infinity.

	  b. The decrease function would reposition the vertex in the
	     priority queue if it is already in the queue

	     i. One way to reposition a vertex is to push it up the
	        priority queue. The drawback to this approach is that
		we need to be able to locate the vertex in the binary
		heap, which means we need to find its location in the
		array representing the binary heap. This means that we
		need to add a field to a vertex that denotes its location
		in the binary heap. Keeping this field up-to-date can be
		rather messy.

	    ii. A second way to reposition a vertex is to simply insert
	        it into the queue again. This means that multiple copies
		of the vertex may be in the queue so after the first copy
		is removed, we don't want to process the remaining copies.
		This means that after deletemin returns a vertex, we should
		check to see whether the vertex is known. If it is we
		repeat the deletemin operation until we obtain an unknown
		vertex.

	   iii. The second approach is easier to implement than the first
	        approach but it may be less efficient because the priority
		queue could contain up to |E| entries rather than
		|V| entries. This means that inserts and deletes may require
		log |E| rather than log |V| time. However,
		|E| < |V|2 so log |E| < 2 log |V|. Hence the
		running time of the second approach should only be a
		constant time slower than the first approach. It is unlikely
		that the priority queue will grow as large as |E| so in
		practice the second approach may be faster, because its
		code is simpler. If I were implementing the shortest path
		problem in practice, I would implement approach 2 first because
		it is easier. Only if I had a performance problem would
		I implement the first approach, and I would do that only
		after using a profiler to ensure that the performance problem
		was caused by the second approach

      3. If the number of edges, E, is proportional to V2, then 
          priority-first using priority queues is O(V2 lg V). 

	  a. Using adjacency matrices, we can get O(V2) running time

	  b. Insight: Each time we choose a vertex from the fringe, we will
	     have to update O(V) priorities. In other words, in all likelihood,
	     we will have to examine almost all the other vertices in the 
	     graph.

          c. Translating this insight to an idea: Since we probably have to 
	     look at most of the other vertices anyways, we might as well 
	     visit all the vertices. Visiting all the vertices is still
	     O(V) time. However, we can both:

	     1. update the costs of the vertices that are adjacent to this 
		newly visited vertex, and

	     2. figure out which vertex to visit next by keeping track of the
		minimum cost vertex

	  d. Each time we visit a vertex, we will scan through the row in the
	      adjacency matrix for that vertex. For each adjacent vertex, w,
	      (i.e., each unknown vertex w with an edge to v), we will
	 
	     i. update w's dist entry

	    ii. keep track of the minimum cost vertex we have visited 
	       (this will be the vertex with the smallest value)

II. Spanning Trees

    A. Spanning Tree of a Graph: A subgraph that contains all the vertices
	of the graph, but only enough edges to form a tree.

    B. Minimum Spanning Tree Problem: Find a set of edges that connect
	all the vertices of a graph such that the sum of the weights of the
	the edges is at least as small as the sum of the weights of any other
	collection of edges connecting all the vertices. 

        1. Sample Application: Wire the cable outlets in a home using the
          least amount of cable possible

    C. Important Property/Insight: Given any division of the vertices of
	  a graph into two sets, the minimum spanning tree contains the 
	  shortest of the edges connecting a vertex in one of the sets to a 
	  vertex in the	other set.

    D. Converting this insight to an algorithm: 

	1. Begin with an arbitrary vertex in the graph

	2. Choose the minimum weight edge emanating from this vertex
	
	3. Add the vertex at the other end of this edge to the set of
		visited vertices (i.e., to the set of tree vertices).

	4. Repeat steps 2 and 3, always choosing the lowest cost edge
		connecting a tree vertex to a fringe vertex.

    E. Nailing down the algorithm even further: The search described in
	B is really a priority-first search of the graph--at each step
	we visit the fringe vertex with the minimum weight edge to a
	tree vertex. We can use the same code we did for the shortest
	path problem except that now the calculation is:

	m.dist = min(m.dist, edge_weight(k,m))
	
	In other words, now we simply want the lowest cost edge between
	m and the vertices currently in the spanning tree.

    F. This algorithm is called Prim's algorithm in honor of its creator.