最短路径算法:迪杰斯特拉(Dijkstra)和弗罗伊德(Floyd)

  关于迪杰斯特拉(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;
}




  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值