关于迪杰斯特拉(Dijkstra)和弗罗伊德(Floyd)算法的思想,严蔚敏老师书上,还有网络上一些博客,讲的比较清楚了,这里只一并给出其实现及驱动源码,注释也比较详细。算法思想非本人原创,只是做一些实现上的小技巧而已,欢迎大家交流、转载,转载请注明出处。我的邮箱:zhangjun03402@163.com。
#include<iostream>
#include<cstring>
using namespace std;
const int INFINITE=65535; //无穷大
const int MAX_VERTEX_NUM=20; // 最大顶点数
typedef struct {
char vexs[MAX_VERTEX_NUM]; //顶点名字,不超过9个字符
int matrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //二维数组充当矩阵,值表示权值
int vexnum, edgenum; //图的当前顶点数和弧数
}MGraph;
//为简单起见,采用严蔚敏老师P188的G6,只是这里把顶点到自身的距离设为0,感觉更合理
int adjmatrix[6][6] = {
0, INFINITE,10, INFINITE,30, 100,
INFINITE,0, 5, INFINITE,INFINITE,INFINITE,
INFINITE,INFINITE,0, 50, INFINITE,INFINITE,
INFINITE,INFINITE,INFINITE,0, INFINITE,10,
INFINITE,INFINITE,INFINITE,20, 0, 60,
INFINITE,INFINITE,INFINITE,INFINITE,INFINITE,0,
};
void CreateDN(MGraph &G)
{
G.vexnum = 6;
G.edgenum = 6;
for(int i=0; i<G.vexnum; i++)
for(int j=0; j<G.vexnum; j++)
G.matrix[i][j] = adjmatrix[i][j];
//顶点名为a,b,c...
for(int i=0; i<G.vexnum; i++){
G.vexs[i] = 'a'+i;
}
}
//查找名为v的顶点的下标
int LocateVex(MGraph &G, char v)
{
int i;
for(i=0;i<G.vexnum;i++)
{
if(G.vexs[i] == v)
return i;
}
return -1;
}
void ShortestPath_DIJ(MGraph &G, char v)
{
int i,j,k,min,_2nd;
int final[MAX_VERTEX_NUM];//该数组用来标识顶点是否已确定了最短路径
int dist[MAX_VERTEX_NUM];
char path[MAX_VERTEX_NUM][MAX_VERTEX_NUM] = {0};
int idx=-1;
//查找名为v的顶点的下标
if((idx=LocateVex(G, v)) < 0)
cout<<"The vertex you input is not existed!"<<endl;
for(i=0;i<G.vexnum;i++)
{ //初始化工作
dist[i]=G.matrix[idx][i];//dist数组用来存储当前找到的v到其他各顶点的最短路径
if(dist[i]<INFINITE && dist[i]!=0){
path[i][0]=v;
path[i][1]=G.vexs[i];//如果v到i有边的话,把顶点字符存到path字符数组中,表示路径
}
final[i]=0;//初始化标识数组为0
}
//dist[idx]=0;
final[idx]=1;
for(j=1;j<G.vexnum;j++)
{
min=INFINITE;
for(i=0;i<G.vexnum;i++)
if(dist[i]!=0 && dist[i]<min && final[i]==0)
{
min=dist[i];
k=i;
}//找到dist数组中最小值的位置k
final[k]=1;//表明已是最短路径集中的顶点
for(i=0;i<G.vexnum;i++)
{//若从源点经过顶点k到顶点i的路径,比dist[i]小,则更新顶点dist[i]
if(dist[i]>dist[k]+G.matrix[k][i] && final[i]==0)
{
dist[i]=dist[k]+G.matrix[k][i];
//下面就是把当前顶点的路径变为最短路径,然后在路径上加上自己
//for(_2nd=0; path[k][_2nd]!='\0'; _2nd++)
//path[i][_2nd]=path[k][_2nd];
//path[i][2nd] = G.vexs[i];
strcpy(path[i], path[k]);
int k;
for(k=0; path[i][k]!='\0'; k++)
;
path[i][k]=G.vexs[i];
}
}//从整体上来看就是算出k的邻接点的当前最短路径
}
cout<<"Start from vex: "<<v<<endl;
cout<<"destination\tpath\tpath_len"<<endl;
for(i=0;i<G.vexnum;i++)
{
if(i==idx)
continue;
cout<<G.vexs[i]<<"\t";
//for(_2nd=0; path[i][_2nd] != '\0'; _2nd++)
//cout<<path[i][_2nd]<<" ";
cout<<"\t"<<path[i]<<" ";
cout<<"\t"<<dist[i]<<endl;
}
}
int path[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
void output(MGraph &G, char start, char end){
int i,j;
i=LocateVex(G, start);
j=LocateVex(G, end);
if(i==j) return;
if(path[i][j]==0)
cout<<G.vexs[j]<<" ";
else
{
output(G,G.vexs[i],G.vexs[ path[i][j] ]);
output(G,G.vexs[ path[i][j] ],G.vexs[j]);
}
}
void ShortestPath_FLOYD(MGraph &G)
{
int i,j,k;
int D[G.vexnum][G.vexnum];
for(i=0;i<G.vexnum;i++)
for(j=0;j<G.vexnum;j++)
{
D[i][j]=G.matrix[i][j];
path[i][j]=0;
}
for(k=0;k<G.vexnum;k++)
for(i=0;i<G.vexnum;i++)
for(j=0;j<G.vexnum;j++)
if(D[i][k]+D[k][j]<D[i][j])
{
D[i][j]=D[i][k]+D[k][j];
path[i][j]=k;
}
}
int main()
{
MGraph G;
char start,end;
int i,j;
CreateDN(G);
cout<<"The vexs in the graph:"<<endl;
for(int i=0; i<G.vexnum; i++){
cout<<G.vexs[i]<<" ";
}
cout<<endl;
cout<<"Input the vex you want to start(Dijkstra):";
cin>>start;
ShortestPath_DIJ(G,start);
cout<<endl;
cout<<"Input the start&end vex(Floyd):"<<endl;
while(cin>>start>>end){
ShortestPath_FLOYD(G);
cout<<start<<" ";
output(G, start, end);
cout<<endl;
}
system("PAUSE");
return 0;
}
真正的Floyd算法其实是一种基于DP(Dynamic Programming)的最短路径算法。其思想如下:
设图G中n 个顶点的编号为0到n-1。令 dist[i, j, k]表示当路径中包含的最大顶点为k时从i 到j 的最短路径的长度。也就是说dist[i,j,k]这条最短路径所通过的中间顶点最大不超过k。因此,如果G中包含边<i,j>,则dist[i, j, 0] =边<i, j> 的长度;若i=j ,则dist[i,j,0]=0;如果G中不包含边<i,j>,则dist[i, j, 0]= +∞。 dist[i, j, n] 则是从i 到j 的最终最短路径的长度。 对于任意的k>0,通过分析可以得到:中间顶点不超过k 的i 到j的最短路径有两种可能:该路径含或不含中间顶点k。若不含,则该路径长度应为dist[i, j, k-1],否则长度为 dist[i, k, k-1] +c[k, j, k-1]。dist[i, j, k]可取两者中的最小值。也就是说dist[i, j, k]=min{dist[i, j, k-1], dist[i, k, k-1] + dist[k, j, k-1] | i>=0, j>=0, k>0}。这种具有最优子结构性质的问题,可以用动态规划方法来求解。下面是其简单实现,代码如下:
#include <iostream>
using namespace std;
const int INFINITE = 100000;
int dist[6][6][6];
int map[6][6] = {
0, INFINITE,10, INFINITE,30, 100,
INFINITE,0, 5, INFINITE,INFINITE,INFINITE,
INFINITE,INFINITE,0, 50, INFINITE,INFINITE,
INFINITE,INFINITE,INFINITE,0, INFINITE,10,
INFINITE,INFINITE,INFINITE,20, 0, 60,
INFINITE,INFINITE,INFINITE,INFINITE,INFINITE,0,
};
void Floyd_DP(){
int i,j,k;
for(i=0;i<6;i++)
for(j=0;j<6;j++)
dist[i][j][0]=map[i][j];
for(k=1;k<6;k++) //k=0时,上面已经做了初始化
for(i=0;i<6;i++)
for(j=0;j<6;j++)
{
if(dist[i][k][k-1]+dist[k][j][k-1]<dist[i][j][k-1])
dist[i][j][k]=dist[i][k][k-1]+dist[k][j][k-1];
else
dist[i][j][k]=dist[i][j][k-1];
}
}
int main(){
int k,u,v;
Floyd_DP();
cout<<"Input the NO. of start&end vertex:"<<endl;
while(cin>>u>>v){
for(k=0;k<6;k++){
if(dist[u][v][k]==INFINITE)
cout<<"+∞"<<endl;
else
cout<<dist[u][v][k]<<endl;
}
}
system("PAUSE");
return 0;
}