CSCI 260 Fall 2010: Shortest paths

Single source shortest paths

Here we consider a simple algorithm for computing the shortest distance from a single source node to all other nodes in a graph:
create a heap to contain edges in the graph, initially empty
   organized by edge weight so the smallest is in the root
make sure each node has a distance field
   (indicating its distance from the start node)
   and initialize that to infinity
pick your start node
   set its distance to zero
   insert all its edges into the heap
while the heap isn't empty
   remove the next edge from the heap
       (i.e. the smallest currently available edge)
   suppose the following
       the edge connects nodes x and y
       the edge has weight w
       x's distance is currently set to a
       y's distance is currently set to b
   then we can update as follows
       if a+w < b then set y's distance to a+w
          and insert all of y's edges into the heap
       otherwise, if the graph is bidirectional 
          if b+w < a then set x's distance to b+w
             and insert all of x's edges into the heap
Once the while loop completes, each node's distance is correct (with unreachable nodes still having their distance set at infinity).

To get the "infinite" value we'll use the maximum floating point value we can store. This can be done by #including <climits>, and initializing each node's distance to FLT_MAX.

Note that in the spanning tree notes we created a heap class for storing edges, so we'll reuse that here.

// EXAMPLE: using the adjacency matrix graphs
//          and heaps from earlier notes

// compute and display the distance of each node
//    from a designated source node
// return true if successful, false otherwise
bool distances(int src)
{
   // make sure the node exists
   if (!Graph[src]) return false;

   // initialize every node's distance to infinity
   for (int i = 0; i < VERTICES; i++) {
       if (Graph[i] != NULL) {
          Graph[i]->distance = FLT_MAX;
       }
   }

   // initialize the distance to the start node
   Graph[src]->distance = 0;

   // create a heap big enough to hold all the edges
   heap edgelist(get_numedges());

   // add all the source node's edges to the heap
   for (int d = 0; d < VERTICES; d++) {
       if (Edges[src][d] > 0) {
          edgenode *e = new edgenode;
          if (e) {
             e->src = src;
             e->dest = d;
             e->weight = Edges[src][d];
             edgelist.insert(e);
          }
       }
   }

   // while the heap isn't empty
   //    remove the next (smallest) edge from the heap
   //    if the edge gives us a new shorter path
   //       to the destination vertex then:
   //          update the distance to that vertex
   //          and insert all it's vertices into the graph
   //    (if the graph is bidirectional we could do similarly
   //     in the other direction)
   edgenode *e;
   node *s, *d;
   while ((e = edgelist.remove()) != NULL) {
      s = Graph[e->src];
      d = Graph[e->dest];
      float w = e->weight;
      if (!s || !d) {
         delete e;
         continue;
      }
      if (d->distance > (s->distance + w)) {
         for (int j = 0; j < VERTICES; j++) {
             if (Edges[e->dest][j] > 0) {
                edgenode *new_e = new edgenode;
                if (new_e) {
                   new_e->src = e->src;
                   new_e->dest = j;
                   new_e->weight = Edges[e->src][j];
                   edgelist.insert(new_e);
                }
             }
         }
      }
      delete e;
   }

   // go through the graph, displaying the distance to each node
   for (int i = 0; i < VERTICES; i++) {
       if (Graph[i] && (i != src)) {
          cout << src << "->" << i << " distance: ";
          if (Graph[i]->distance == FLT_MAX) cout << "infinite" << endl;
          else cout << Graph[i]->distance << endl;
       }
   }
}

All pairs shortest paths

One way to find the shortest distance between every pair of vertices in a graph is to take the single-source algorithm shown above and run it N times, once on each possible starting point in the graph.

Here we consider two additional algorithms for computing the shortest distances between every pair of vertices in a graph.

For simplicity of explanation, we'll assume we're using an adjacency matrix representation for both approaches (though for sparse graphs better efficiency would be obtained through an adjacency list approach).

Both approaches currently only store the actual distance between the pairs of vertices, but can readily be modified to store the collection of intermediate vertices as well.

Just to ease up on complications, let's also assume that VERTICES is actually the number of vertices currently in the graph (i.e. there are no null entries in the matrix, it is just as big as it needs to be).

Let's assume the graph information is stored as follows:

// for N vertices we have an NxN matrix,
//     where ultimately Distance[i][j] will be used
//     to store the shortest distance from i to j
// initially, Distance[i][j] is set to 0 when i == j,
//     to infinity when there is no edge from i to j,
//     and is set to the weight of edge(i,j) otherwise
float Distance[VERTICES][VERTICES];
// ALGORITHM 1: Floyd's Algorithm

// for each possible intermediate vertex,
//     for each possible source vertex,
//         for each possible destination vertex,
//             if the known direct distance from source to destination
//                is larger than the sum of the known distances from
//                source to intermediate and intermediate to destination
//             then update the distance from source to destination
//                using the path through the intermediate vertex

for (int middle = 0; middle < VERTICES; middle++) {
    for (int source = 0; source < VERTICES; source++) {
        for (int destination = 0; destination < VERTICES; destination++) {
            float midpath = Distance[source][middle] + Distance[middle][destination];
            if (midpath < Distance[source][destination])
               Distance[source][destination] = midpath;
        }
    }
}
// ALGORITHM 2: Dijkstra's Algorithm
// (actually Dijkstra's algorithm applied once for each source vertex)

// for each source vertex
//     mark all vertices as unfinished
//     repeat N times
//        find the next unfinished vertex 
//             closest to the source
//        mark the vertex as finished
//             (its distance from the source is now known)
//        for each edge leaving this vertex,
//             if it provides a new (shorter) way
//             to reach another unfinished vertex then
//             update the distance to that vertex

// again, we assume that initially the distance matrix entries are 0, 
//    FLT_MAX, or a positive edge-weight, based on an adjacency matrix,
// and again we assume VERTICES is the actual number of vertices in the
//    graph - there are no null entries
for (int source = 0;  source < VERTICES; source++) {

    // mark all vertices as unfinished
    bool finished[VERTICES];

    for (int a = 0; a < VERTICES; a++)
        finished[a] = false;

    // process n vertices, each time getting the next closest one
    //    to the source
    for (int n = 0; n < VERTICES; n++) {

        // go through all n vertices, looking for the closest
        //    one that is not yet finished
        int closest = -1;
        float distance = FLT_MAX;
        for (int j = 0; j < VERTICES; j++) {
            if ((finished[j] == false) && (Distance[source][j] < d)) {
               distance = Distance[source][j];
               closest = j;
            }
        }

        // mark the selected vertex as finished
        // (the distance to this vertex is now known)
        finished[closest] = true;

        // examine each edge going out from the selected vertex
        //    to unfinished vertices, and update the distance to
        //    the unfinished vertex is appropriate
        for (int current = 0; current < VERTICES; current++) {

            if (finished[current] == false) {

               // compute the distance to the current vertex based on a path
               //    from the course to closest then to current
               int intermediate = Distance[source][closest] + Distance[closest][current];

               // if the path through the intermediate vertex is shorter than
               //    the previous distance to current 
               // then update the distance to current
               if (Distance[source][current] > intermediate)
                  Distance[source][current] = intermediate;

            }
        }
    }
}