最短路径
最短路径问题: 给定带权的有向图G=(V,E),从某顶点出发,沿图的边到达另一个顶点所经过的路径中,各边上权值之和最小的一条路径称为最短路径。 解决最短路径问题可以采用Dijstra算法和Floyd算法,其中Floyed算法可以求解任意两点间最短路径的长度。
一.Dijstra算法
Dijstra算法是按路径长度的非递减次序逐一产生最短路径的算法:首先求得长度最短的一条最短路径,再求得长度次短的一条最短路径,依次类推,直到从源点到其它所有顶点之间的最短路径都已求得为止。
设集合S存放已经求得最短路径的终点,则V-S为尚未求得最短路径的终点。初始状态下集合S中只有一个源点,设为v0。Dijstra的具体做法是:首先产生源点v0到自身的路径,长度为0,将v0加入S;算法的每一步,按照最短路径值的非减次序产生下一条最短路径,并将该路径的终点t加入S;直到S=V,算法结束。
1.数据结构: a) 一维数组d,d[i]中存放从源点v0到i的当前最短路径的长度,该路径上除顶点i以外,其余顶点都属于S,并且这是所有这些路径中的最短者。 b) 一维整型数组path,path[i]中存放从v0到顶点i的最短路径上,位于顶点i前面的那个顶点。 c) 一维布尔数组s,若s[i]为true表示顶点i再S中,否则表示i再V-S中。
2.算法实现(Java):
public class Dijkstra {
//选出最小的d[k]
public static int choose(double[] d,boolean[] s){
double min=Double.POSITIVE_INFINITY;
int minpos=-1;
int n=d.length;
for(int i=0;i<n;i++){
if(d[i]<=min && !s[i]){
min=d[i];
minpos=i;
}
}
return minpos;
}
//v表示源点,a为图的邻接矩阵,d和path含义如上所述
public static void dijkstra(int v,double[][] a,double[] d,int[] path){
if(d==null)return;
int n=a.length;
if(n==0||n!=a[0].length||v<0||v>n-1)return;
//s[i]为true表示顶点i在S中,否则表示i在V-S中
boolean[] s=new boolean[n];
//初始化操作s[i]初始化为false,d[i]=a[v][i]
for(int i=0;i<n;i++){
s[i]=false;
d[i]=a[v][i];
if(i!=v && d[i]<Double.POSITIVE_INFINITY){
path[i]=v;
}else{
path[i]=-1;
}
}
//将顶点v加入S
s[v]=true;
d[v]=0;
for(int i=1;i<n;i++){
int k=choose(d,s);
s[k]=true;
for(int w=0;w<n;w++){
if(!s[w] && d[k]+a[k][w]<d[w]){
d[w]=d[k]+a[k][w];
path[w]=k;
}
}
}
}
public static void main(String[] args) {
double INFI=Double.POSITIVE_INFINITY;
double[][] a={ { 0,50,10,INFI,70,INFI},{INFI,0,15,INFI,10,INFI},{20,INFI,0,15,INFI,INFI},{INFI,20,INFI,0,35,INFI},{INFI,INFI,INFI,30,0,INFI},{INFI,INFI,INFI,3,INFI,0 } };
double[] d=new double[a.length];
int[] path=new int[a.length];
dijkstra(0,a,d,path);
int k=path[4];
System.out.println(k);
while(k!=0){
k=path[k];
System.out.println(k+"\t");
}
}
}
时间复杂度为O(N^2)
二.Floyd算法
Floyd算法对于求任意两个顶点间的最短距离更加直接。 1.数据结构: a) d[i][j]表示从顶点i到j中,只经过S中的顶点的,所有可能的路径中的最短路径的长度。如果从i到j,中间只经过S中的顶点的当前没有路径想通,那么d[i][j]为一个大值INFINITY。可以称此时的d[i][j]中保存的是从i到j的“当前最短路径”的长度。随着S中顶点数增加,d[i][j]的值不断修正, 当S=V的时候,d[i][j]中的值就是从i到j的最短路径长度。 b) path[i][j]表示从顶点i到j的最短路径上,顶点j的前一个顶点。
2.算法实现(Java):
public class Floyd {
public static void floyd(double[][] d,int[][] path){
if(d==null)return;
int n=d.length;
if(n==0||n!=d[0].length)return;
//初始化
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i!=j && d[i][j]<Integer.MAX_VALUE){
path[i][j]=i;
}else{
path[i][j]=-1;
}
}
}
//更新d和path
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(d[i][k]+d[k][j]<d[i][j]){
d[i][j]=d[i][k]+d[k][j];
path[i][j]=path[k][j];
}
}
}
}
}
public static void main(String[] args) {
double INFINITY=Double.POSITIVE_INFINITY;
double[][] d={ { 0,1,INFINITY,4},{INFINITY,0,9,2},{3,5,0,8},{INFINITY,INFINITY,6,0 } };
int[][] path=new int[4][4];
floyd(d,path);
//0-2的最短路径
System.out.print(2+"\t");
int k=path[0][2];
System.out.print(k+"\t");
while(k!=0){
k=path[0][k];
System.out.print(k+"\t");
}
}
}