views:

72

answers:

1

I am writing a Graph library that has both adjacency list and matrix implementations. Here is some code I came across in a Java data structures textbook:

static void floyd(Graph<V,E> g) 
// post: g contains edge (a,b) if there is a path from a to b 
{
     Iterator<V> witer = g.iterator();   //vertex iterator
     while (witer.hasNext())
     {
        Iterator<V> uiter = g.iterator(); 
        V w = witer.next(); 
        while (uiter.hasNext())
        {
            Iterator<V> viter = g.iterator();
            V u = uiter.next(); 
            while (viter.hasNext())
            {
                V v = viter.next(); 
                if (g.containsEdge(u,w) && g.containsEdge(w,v)) 
                {
                     Edge<V,E> leg1 = g.getEdge(u,w);
                     Edge<V,E> leg2 = g.getEdge(w,v); 
                     int leg1Dist = leg1.label(); 
                     int leg2Dist = leg2.label(); 
                     int newDist = leg1Dist+leg2Dist;

                     if (g.containsEdge(u,v)) 
                     {
                         Edge<V,E> across = g.getEdge(u,v); 
                         int acrossDist = across.label(); 
                         if (newDist < acrossDist)
                           across.setLabel(newDist); 
                      } 
                      else 
                      {
                           g.addEdge(u,v,newDist);
                       }
                  }
             }
         }
     }

But it seems like it is just overwriting the current edge with with the "shortest". Is this interpretation correct? I could use some clarification here.

Note: Here is some of the Edge class:

public class Edge
{
/**
 * Two element array of vertex labels.
 * When necessary, first element is source.
 */
protected Object[] vLabel;  // labels of adjacent vertices
/**
 * Label associated with edge.  May be null.
 */
protected Object label;     // edge label
/**
 * Whether or not this edge has been visited.
 */
protected boolean visited;  // this edge visited
/**
 * Whether or not this edge is directed.
 */
protected boolean directed; // this edge directed

/**
 * Construct a (possibly directed) edge between two labeled
 * vertices.  When edge is directed, vtx1 specifies source.
 * When undirected, order of vertices is unimportant.  Label
 * on edge is any type, and may be null.
 * Edge is initially unvisited.
 *
 * @post edge associates vtx1 and vtx2; labeled with label
 *       directed if "directed" set true
 *
 * @param vtx1 The label of a vertex (source if directed).
 * @param vtx2 The label of another vertex (destination if directed).
 * @param label The label associated with the edge.
 * @param directed True iff this edge is directed.
 */
public Edge(Object vtx1, Object vtx2, Object label,
            boolean directed)
{
    vLabel = new Object[2];
    vLabel[0] = vtx1;
    vLabel[1] = vtx2;
    this.label = label;
    visited = false;
    this.directed = directed;
}

/**
 * Returns the first vertex (or source if directed).
 *
 * @post returns first node in edge
 * 
 * @return A vertex; if directed, the source.
 */
public Object here()
{
    return vLabel[0];
}

/**
 * Returns the second vertex (or source if undirected).
 *
 * @post returns second node in edge
 * 
 * @return A vertex; if directed, the destination.
 */
public Object there()
{
    return vLabel[1];
}

/**
 * Sets the label associated with the edge.  May be null.
 *
 * @post sets label of this edge to label 
 * 
 * @param label Any object to label edge, or null.
 */
public void setLabel(Object label)
{
    this.label = label;
}

/**
 * Get label associated with edge.
 *
 * @post returns label associated with this edge
 * 
 * @return The label found on the edge.
 */
public Object label()
{
    return label;
}
+3  A: 

It would be easier to understand what you're doing if you use a matrix to store the result in a matrix. Consider the following simple weighted graph:

       2
1 +---------+ 2
  |\        |
  | -\      |
3 |   -\5   | 2
  |     -\  |
  |       -\|
3 +---------+ 4
        4

Now consider this implementation of the Floyd-Warshall algorithm:

public Matrix floyd() {
  Matrix m = new Matrix(numVertices, numVertices, Integer.MAX_VALUE);
  for (int i = 1; i<=numVertices; i++) {
    EdgeNode edge = edges[i];
    while (edge != null) {
      m.setData(i, edge.getY(), edge.getWeight());
      edge = edge.getNext();
    }
    m.setData(i, i, 0);
  }
  for (int i = 1; i <= numVertices; i++) {
    for (int j = 1; j <= numVertices; j++) {
      for (int k = 1; k <= numVertices; k++) {
        if (m.getData(i, j) < Integer.MAX_VALUE && m.getData(i, k) < Integer.MAX_VALUE) {
          int through = m.getData(i, j) + m.getData(i, k);
          if (through < m.getData(j, k)) {
            m.setData(j, k, through);
          }
        }
      }
    }
  }
  return m;
}

The first part of this seeds the matrix result with Integer.MAX_VALUE. Putting 0 here would yield an incorrect result but using values of 1 and 0 (respectively) would work fine for an unweighted graph. Integer.MAX_VALUE is there simply for correct minimal value checks.

The second part is the key part of the algorithm. It looks at the distance between two points (i,k) comparing it to the distance of (i,j) + (j,K) for all vertices j. If the indirect path is less it is substituted into the matrix as the shortest path and so on.

The result of this algorithm on the above (very simple) graph is:

[ 0 2 3 5 ]
[ 2 0 5 3 ]
[ 3 5 0 4 ]
[ 5 3 4 0 ]

What this tells you is the shortest distance between any pair of vertices. Note: I've seeded the result of (i,i) to 0 as you can argue the distance of any node to itself is 0. You can take out that assumption easily enough, yielding this result:

[ 4 2 3 5 ]
[ 2 4 5 3 ]
[ 3 5 6 4 ]
[ 5 3 4 6 ]

So node 3 to itself is a distance of 6 as it traverses 3->1->3 as the shortest path. This would get a lot more interesting in a directed graph, which Floyd's can handle.

Floyd's is an O(n3) algorithm. It won't reconstruct the actual path between each pair of points, just the total distance (weight). You can use Dijkstra's algorithm between each vertex pair to construct the actual paths, which interestingly enough is also O(n3) but tends to be slower in real world usage as the calculations of Floyd's are pretty simple.

Your algorithm uses an adjacency list instead of a matrix to implement this algorithm, which confuses the issue slightly. My version uses Integer.MAX_VALUE as a sentinel value to indicate no path (yet calculated) whereas yours uses the absence of an edge for the same thing. Other than that, it's exactly the same.

cletus
I appreciate your thoroughness here, however my confusion is at the `if(g.containsEdge(u,v))` statement to the bottom. Specifically, it seems like it is doing the converse of Floyd's algorithm by looking for the edges (u,w) and (w,v) and comparing that to an "unconsidered" edge (u,v). Rather than looking at edges (u,w) and (w,v) and comparing the sum to edge (u,v) I'm just not sure whether this is still a valid method or not. Is this assessment correct?
Nathan
#Nathan my versions uses `Integer.MAX_VALUE` to indicate no path. Your versions uses the lack of an edge to indicate no path so `g.containsEdge()` is asking "is there a path between these nodes?". Not a direct path necessarily. Just any path.
cletus