import java.util.Vector;
import java.util.StringTokenizer;/**
 * 这是一个点对点(S-T)Dijkstra算法的改进。用于求两点间的所有最短路径。
 * 
 * @author Fe
 */
public class STDijkstraAdv {
 /**
  * 所求路经所在图的邻接链表引用。
  */
 GraphAdjList graphAdjList[] = null;
 /**
  * 图中总的结点数。
  */
 int totalNodeNum;
 /**
  * 源结点和目标结点。
  */
 int sourceNode, targetNode;
 /**
  * 两结点间的路径长度。
  */
 int distance = -1;
 /**
  * 前驱结点链表数组。
  */
 PreNodes preNodes[] = null;
 
 /**
  * 用一个图的邻接表引用构造一个实例引用。
  * @param graph
  * 图的邻接表引用
  */
 public STDijkstraAdv(GraphAdjList graph[]) {
  graphAdjList = graph;
  totalNodeNum = graphAdjList.length;
  
  preNodes = new PreNodes[totalNodeNum];
 }
 
 /**
  * 用一个图的邻接表以及源结点和目标结点构造一个实例引用。
  * @param graph
  * 图的邻接表引用
  * @param source
  * 源结点
  * @param target
  * 目标结点
  */
 public STDijkstraAdv(GraphAdjList graph[], int source, int target) {
  graphAdjList = graph;
  totalNodeNum = graphAdjList.length;
  preNodes = new PreNodes[totalNodeNum];
  
  setST(source, target);
 }
 
 /**
  * 设置源结点和目标结点。用于求不同的结点间的最短路径。
  * @param s
  * 源结点
  * @param t
  * 目标结点
  */
 public void setST(int s, int t) {
  sourceNode = s;
  targetNode = t;
  
  for (int i = 0; i < totalNodeNum; i++) {
   preNodes[i] = new PreNodes();
   preNodes[i].preNode = sourceNode;
  }
 }
 
 /**
  * 返回所求最短路径的路径长度。
  * @return
  * 所求路径的总路径长度
  */
 public int getDistance() {
  return distance;
 }
 
 /**
  * 返回从结点s到结点t得最短路径长度。
  * @param s
  * 源结点
  * @param t
  * 目标结点
  * @return
  * 所求得的最短路径长度
  */
 public int getDistance(int s, int t) {
  setST(s, t);
  go();
  
  return distance;
 }
 
 /**
  * 返回所求结点间最短路径子图。
  * 用二维数组表示。
  * 每一行表示一条最短路径。
  * @return
  * 所求结点间最短路径构成的最短路径子图。
  */
 public int[][] getPath() {
  //用深度优先法从前驱表中求出每条最短路径。
  
  PreNodes temp = preNodes[targetNode];
  Vector stack = new Vector();
  boolean firstTime = true;
  
  //用于存储每次求得的一条最短路径。
  Vector v = new Vector();
  v.add(new Integer(targetNode));
  
  //以0d1dt2d3d4dt的形式存储所有最短路径。
  //d表示每条最短路径内每个节点的分隔符,t表示各条最短路径的分隔符。
  String allPathTemp = new String("");
  int pathNum = 0;
  
  while (!stack.isEmpty() || firstTime) {
   if (!firstTime) {
    //branchNode用于表示上一次路径分支点的那个节点的节点号。
    Integer branchNode = (Integer)(stack.remove(stack.size()-1));
    //分支节点的节点前驱信息。
    temp = (PreNodes)stack.remove(stack.size()-1);
    //在上一条最短路径中,将在分支点以后的节点号删除。
    int i = v.indexOf(branchNode);
    for (int j = v.size(); i < j; j--) {
     v.remove(j-1);
    }
   }
   //找一条最短路径。
   while (((Integer)v.get(v.size()-1)).intValue() != sourceNode) {
    v.add(new Integer(temp.preNode));
    if (temp.anotherPreNode != null) {
     stack.add(temp.anotherPreNode);
     stack.add(new Integer(temp.preNode));
    }
    temp = preNodes[temp.preNode];
   }
  
   //将求得的最短路径按0d1dt2d3d4dt的形式存放在allPathTemp中。
   //当求得所有最短路径后再解析。
   for (int i = 0; i < v.size(); i++) {
    allPathTemp += (((Integer)(v.get(i))).toString() + "d");
   }
   allPathTemp += "t";
   pathNum++;
   
   firstTime = false;
  }
  
  //存放所有最短路径的数组。
  int path[][] = new int[pathNum][];
  //以2d3d4dt的形式存放一条最短路径的数组。等待进一步解析。
  String pathTemp[] = new String[pathNum];
  
  //第一步解析,根据t将各条最短路径分开。
  pathTemp = filterAllPathTemp(allPathTemp, pathNum);
  for (int i = 0; i < pathNum; i++) {
   //第二步解析,根据d将每条最短路径内的各个结点分开。
   path[i] = filterPathTemp(pathTemp[i]);
  }
  
  return path;
 }
 
 //第一步解析,根据t将各条最短路径分开。
 private String[] filterAllPathTemp(String path, int num) {
  StringTokenizer st = new StringTokenizer(path, "t");
  String[] pathTemp = new String[num];
  
  for (int i = 0; i < num; i++) {
   pathTemp[i] = st.nextToken();
  }
  
  return pathTemp;
 }
 
 //第二步解析,根据d将每条最短路径内的各个结点分开。
 private int[] filterPathTemp(String pathTemp) {
  StringTokenizer st = new StringTokenizer(pathTemp, "d");
  int nodeNum = 0;
  //求得这条最短路径包含的结点数。
  try {
   while (true) {
    st.nextToken();
    nodeNum++;
   }
  } catch (Exception e) {
  }
  
  st = new StringTokenizer(pathTemp, "d");
  int path[] = new int[nodeNum];
  
  for (int i = 0; i < nodeNum; i++) {
   path[i] = Integer.parseInt(st.nextToken());
  }
  
  return path;
 }
 
 
 public void go(int s, int t) {
  setST(s, t);
  go();
 }
 
 //暂不考虑非连同情形,待改进。
 public void go() {
  //用于存储从源结点到其他各个结点的最短路径长度的temp。
  int distanceTemp[] = new int[totalNodeNum];
  //初始时将从源结点到其他各个结点的最短路径长度全都设置为无穷远。除源结点到自身的长度为0。
  for (int i = 0; i < totalNodeNum; i++) {
   distanceTemp[i] = 0X0FFFFFFF;
  }
  distanceTemp[sourceNode] = 0;
  
  //根据图的邻接表具有的信息,将于源结点邻接的结点的路径长度设置为边的权值,其余不变,作为长度初始值。
  NextAdjNode temp = null;
  temp = graphAdjList[sourceNode].firstNode;
  distanceTemp[temp.nodeNum] = temp.edgeWeight;
  while (temp.nextNode != null) {
   temp = temp.nextNode;
   distanceTemp[temp.nodeNum] = temp.edgeWeight;
  }
  
  int nextNodes[] = new int[1];
  nextNodes[0] = sourceNode;
  //如果目的节点没有包含在找到的节点当中,则继续找。
  while (!containTargetNode(nextNodes)) {
   nextNodes = chooseNextPathNode(distanceTemp, distanceTemp[nextNodes[0]]);
   updateDistance(nextNodes, distanceTemp);
  }
  distance = distanceTemp[targetNode];
 }
 
 private boolean containTargetNode(int nextNodes[]) {
  if (nextNodes == null) {
   return false;
  }
  for (int i = 0; i < nextNodes.length; i++) {
   if (nextNodes[i] == targetNode)
    return true;
  }
  return false;
 }
 
 private int[] chooseNextPathNode(int distanceTemp[], int currentPathLength) {
  int temp = 0X0FFFFFFF;
  Vector nextNodeTemp = new Vector();
  //从源结点到每个其余节点的距离进行比较,在这个距离大于上一次的最小距离的前提下,
  //如果源结点到某个结点距离是最小的,则修改最小距离,并将着这节点设为下一个找到的结点;
  //如果源结点到某个结点的距离与目前找到得最短距离相等,则追加到下一次找到的结点数组中。
  for (int i = 0; i < totalNodeNum; i++) {
   if ((temp > distanceTemp[i]) && (distanceTemp[i] > currentPathLength) && (i != sourceNode)) {
    temp = distanceTemp[i];
    nextNodeTemp.clear();
    nextNodeTemp.add(new Integer(i));
   } else if (temp == distanceTemp[i]) {
    nextNodeTemp.add(new Integer(i));
   }
  }
  
  int nextNodes[] = new int[nextNodeTemp.size()];
  for (int i = 0; i < nextNodeTemp.size(); i++) {
   nextNodes[i] = ((Integer)nextNodeTemp.get(i)).intValue();
  }
  
  return nextNodes;
 }
 
 private void updateDistance(int currentNodes[], int distanceTemp[]) {
  //根据本次找到的结点数组,对源结点到各点的距离进行更新。
  //如果距离变小了,就修改这个距离和这个结点的前驱结点。
  //如果距离与原来的距离相等,则将本次找到的节点数组中的某个结点追加到这个结点的前驱结点链表中。
  for (int i = 0; i < currentNodes.length; i++) {
   NextAdjNode temp = null;
   if (graphAdjList[currentNodes[i]] != null) {
    temp = graphAdjList[currentNodes[i]].firstNode;
   }
  
   while (temp != null) {
    if (distanceTemp[currentNodes[i]] + temp.edgeWeight < distanceTemp[temp.nodeNum]) {
     distanceTemp[temp.nodeNum] = distanceTemp[currentNodes[i]] + temp.edgeWeight;
   
     preNodes[temp.nodeNum].anotherPreNode = null;
     preNodes[temp.nodeNum].preNode = currentNodes[i];
    } else if (distanceTemp[currentNodes[i]] + temp.edgeWeight == distanceTemp[temp.nodeNum]) {
     append(preNodes[temp.nodeNum], currentNodes[i]);
    }
   
    temp = temp.nextNode;
   }
  }
 }
 
 private void append(PreNodes pre, int NodeAppended) {
  while(pre.anotherPreNode != null) {
   pre = pre.anotherPreNode;
  }
  pre.anotherPreNode = new PreNodes();
  pre.anotherPreNode.preNode = NodeAppended;
 }
}/**
 * 前驱结点链表的数据结构定义。
 * @author Fe
 */
class PreNodes {
 int preNode = -1;
 PreNodes anotherPreNode = null;
}