private ArrayList<Vertex<T>> getPath(
      ArrayList<Tuple<Pair<Vertex<T>>, Double>> vertexPairs,
      Vertex<T> src,
      Vertex<T> dest ) 
	{
    ArrayList<Vertex<T>> path = new ArrayList<Vertex<T>> ();
    Vertex<T> successor = dest;
    Pair<Vertex<T>> tempPair = null;

    // trace the path backwards from the destination to the source by
    // finding each vertex's minimum cost predecessor in this path.
    while ( successor != src ) {
      double minCost = Double.MAX_VALUE; // assume the worst :-(

      // now find the predecessor on the cheapest path to successor
      for ( int i = 0; i < vertexPairs.size(); i++ ) {
        Tuple<Pair<Vertex<T>>, Double> tuple = vertexPairs.get( i );
        Pair<Vertex<T>> pair = tuple.getFirstElement();

        // got another candidate vertex tuple, see if this one
        // has a cheaper cost than another other path to successor
        // seen so far
        if ( pair.getSecondElement().equals( successor ) ) {

          // it is possible there are multiple "cheapest" paths
          // to successor; want to avoid adding duplicates
          if ( !path.contains( successor ) ) {
            path.add( successor );
          }
          double cost = tuple.getSecondElement();
          if ( cost < minCost ) {
            minCost = cost; // new lowest cost path to successor
            tempPair = pair;
          }

          // remove it so we never need to look at it again
          // assumes the caller isn't going to need this list
          // intact!
          vertexPairs.remove( i );
        }
      }

      // the predecessor for successor becomes the new successor,
      // as we move backward in the path from destination to source
      successor = tempPair.getFirstElement();
    }

    path.add( src );

    // vertices added in reverse order, fix that
    Collections.reverse( path );

    return path;
  }