最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)

最短路径

求最短路径重要性不言而喻,下面直接分析两个算法。
分类:
1:从某个源点到其余个点的最短路径
迪杰斯特拉(Dijkstra)算法
2:每一对之间的最短路径
弗洛伊德(Floyd)算法

一:存储结构

邻接矩阵(这里不再重复讲了,请参考):
https://blog.csdn.net/weixin_39956356/article/details/80470091

相关代码:

//邻接矩阵
#define INT__MAX 65000 //最大值65535,表示两顶点没有联系
#define MAX_VERTEX_NUM 20 //最多顶点数

typedef char VertexType;
typedef int  EdgeType;


//顶点信息
typedef struct ArcCell {
    EdgeType wight;

}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];                //二维数组 

                                                                    //弧的信息
typedef struct MGraph {
    VertexType vexs[MAX_VERTEX_NUM];                                //顶点向量 
    AdjMatrix  arcs;                                                //邻接矩阵 
    int vexnum, arcnum;                                             //图的当前顶点数和弧数 
}MGraph;

创建有向图

注意一下(有向图):G.arcs[i][j].wight = w;

相关代码:

//建立无向图的邻接矩阵 
void CreatMGraph(MGraph &G)
{
    for (int i = 0; i < G.vexnum; i++) {
        printf("Please enter %d data:", i + 1);
        scanf(" %c", &G.vexs[i]);                                           //输入顶点值
    }
    for (int i = 0; i<G.vexnum; i++)
        for (int j = 0; j<G.vexnum; j++)
            G.arcs[i][j].wight = INT__MAX;                                  //邻接矩阵初始化 

    VertexType v1, v2;
    EdgeType w;
    int i, j;
    printf("Please input the two data of arc and wight,for example: A C 5\n");
    for (int k = 0; k < G.arcnum; k++) {
        printf("The %d arc: ", k + 1);
        scanf(" %c", &v1);                                                      //输入第一个顶点
        getchar();                                                              //把输入的空格读走
        v2 = getchar();                                                         //输入弧的第二个顶点
        scanf("%d", &w);                                                        //输入权值
        i = LocateVex(G, v1);                                                   //获取弧的第一个节点位置
        j = LocateVex(G, v2);                                                   //获取弧的第二个节点位置

        G.arcs[i][j].wight = w;                                                 //把权值存放在邻接矩阵中
    }
}

二:迪杰斯特拉(Dijkstra)算法,带输出路径

自己将这个算法分成两部分:
一:最短路径长度
二:最短路径

首先,我们先看最短路径长度问题

1:这里需要引入一个辅助数组Dij[],每次遍历一遍数组得到本次数组中最小值及所在位置。
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
2:找到最小值,并入S集
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
3:更新新的路径值
判断条件:还没有并入S集,且有更小的路径值
if (!isGetShortestPath[w] && (min + G.arcs[minIndex][w].wight < Dij[w]))
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

其次,最短路径

我拿一个图来讲解,整个流程。不防举A->E。它的最短路径是A->C->E,我们分析下
1:这里需要引入一个辅助Code数组code[][],记录走过的最短路径,初始值不防赋值为’!‘。
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
2:清空E结点该行
3:把C结点路径拷贝到E中
4:在第一个 ! ,添加尾结点E
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

图6及输出:
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

补充:这里再多说一句,动态二维数组分配

先分配行,后分配列
typedef char **shortestCode;                                        //最短路径编码
/****************************************************************** ** 动态分配二维数组 *******************************************************************/
    code = (shortestCode)malloc(G.vexnum * sizeof(char *));                     //分配G.vexnum行
    for (int i = 0; i < G.vexnum; ++i) {                                        //为每行分配G.vexnum列
        code[i] = (char *)malloc(G.vexnum * sizeof(char));
    }

相关代码

/*********************************************************************************************** ** 迪杰斯特拉(Dijkstra)算法 ** v0 :任给一个起始点 ** &code:最短路径线路(这里的&是必须的,如果没有,相当于传进来没有初始化的实体,编译器报错) ************************************************************************************************/
void ShortestPath_DIJ(MGraph &G, char v0, shortestCode &code)
{
    int v0Index = LocateVex(G, v0);
    bool *isGetShortestPath = (bool *)malloc(G.vexnum * sizeof(bool));          //是否并入S集
    int *Dij = (int *)malloc(G.vexnum * sizeof(int));                           //最短路径和

    /****************************************************************** ** 动态分配二维数组 *******************************************************************/
    code = (shortestCode)malloc(G.vexnum * sizeof(char *));                     //分配G.vexnum行
    for (int i = 0; i < G.vexnum; ++i) {                                        //为每行分配G.vexnum列
        code[i] = (char *)malloc(G.vexnum * sizeof(char));
    }

    for (int i = 0; i < G.vexnum; i++) {                                        //初始所有点都不并入S集,把顶点的权值赋给Dij[]
        isGetShortestPath[i] = false;
        Dij[i] = G.arcs[v0Index][i].wight;

        for (int w = 0; w < G.vexnum; w++)
            code[i][w] = '!';                                                   //设code[][]初值为!,即没有路径 
        if (Dij[i] < INT__MAX)                                                  
        {
            code[i][0] = v0;                                                    //到i最短路径经过的第一个顶点是v0
            code[i][1] = G.vexs[i];                                             //到i最短路径经过的第二个顶点是i
        }
    }

    isGetShortestPath[v0Index] = true;                                          //把顶点并入S集

    int min, minIndex;
    for (int i = 1; i < G.vexnum; i++) {
        min = INT__MAX;                                                         //不妨把min放置为最大值
        /****************************************************************** ** 获取Dij数组中最小值-----这是其他还没有并入S集 路径上的一个子集 ** 对应的数组位置----------为方便下面从这结点出发的弧遍历找最小值 *******************************************************************/
        for (int w = 0; w < G.vexnum; w++) {
            if (!isGetShortestPath[w])
                if (Dij[w] < min)
                {
                    minIndex = w;
                    min = Dij[w];
                }
        }

        isGetShortestPath[minIndex] = true;                                     //把位置为minIndex的结点并入S集

        for (int w = 0; w < G.vexnum; w++) {                                    //如果有更小的路径,替换原来的路径和最小值
            if (!isGetShortestPath[w] && (min + G.arcs[minIndex][w].wight < Dij[w]))
            {
                Dij[w] = min + G.arcs[minIndex][w].wight;                       //更新Dij[]值

                for (int j = 0; j < G.vexnum; j++) {                            //如果Dij[]值改变,新节点路径清空
                    code[w][j] = '!';
                }
                for (int j = 0; j < G.vexnum; j++) {
                    code[w][j] = code[minIndex][j];                             //把minIndex结点路径全部拷贝到w中去
                    if (code[w][j] == '!')
                    {
                        code[w][j] = G.vexs[w];                                 //在遇到第一个'!',将新节点放在后面
                        break;
                    }
                }
            }
        }
    }

    printf("\n迪杰斯特拉(Dijkstra)最短路径长度与最短路径如下所示:\n");                                                      
    for (int i = 1; i < G.vexnum; i++) {
        if (Dij[i] != INT__MAX)
        {
            printf("从顶点 %c ---> %c 最短路径长度为:%d\t最短路径为:", v0, G.vexs[i], Dij[i]);
            for(int j = 0; j < G.vexnum; j++)
            {
                if(code[i][j] != '!')
                    printf(" %c",code[i][j]);                                   //输出最短路径
            }
            printf("\n");
        }
        else if (Dij[i] == INT__MAX)
        {
            printf("从顶点 %c ---> %c 不可到达!!!\n", v0, G.vexs[i]);      //不存在最短路径
        }
    }

    free(Dij);                                                                  //释放Dij[][]空间
    free(isGetShortestPath);                                                    //释放isGetShortestPath[]空间
}

程序输出

图6及输出:
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
图5及输出:
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

三:弗洛伊德(Floyd)算法,带输出路径

和dijkstra算法一样,自己将这个算法分成两部分:
一:最短路径长度
二:最短路径

首先,我们先看最短路径长度问题

这个算法的最短路径长度核心代码只有5行
1:就是看走哪边短!!!如果短就替换。
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

其次,最短路径(弗洛伊德输出路径要麻烦些)

一:路径的创建

1:Path[ ][ ]初始化

矩阵Path的初值则为各个边的终点顶点—–相当于直接从v到w(上面两种方案的第一种)
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

2:Path[ ][ ]逐渐储存路径

当且仅当通过“另外一个点”的时候,有更短路径,即更新路径Path[v][w],把“另外一个点”放进Path[v][w]
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
这里举个例子:比如A->D,路径是A->E->D
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

二:路径的输出

这里主要是是一个循环,只到输出字母是终点为止。
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
不防先人为的输出起点,和第一个点,剩下的交给while循环
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
最后举个例子:比如A->F,路径是A->E->D->F
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》
需要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图
问题。因为带有“负权回路”的图没有最短路。比如:
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

相关代码

/************************************************************************************************************* ** 弗洛伊德(Floyd)算法 ** 核心:是一种尝试的想法,从v到w只有两种方案: 1:直接从v到w,即DFloyd[v][w] 2:经过“另外的一个点”u,从v到w,即DFloyd[v][u] + DFloyd[u][w] 比较两种方案,取更小的 1:比较 :DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w] 2:取更小的:DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w] ** 这里的Path矩阵的构造很巧,关于Path矩阵代码仅仅只有几行而已!!!下面分析Path矩阵 1:矩阵Path的初值则为各个边的终点顶点-----相当于直接从v到w(上面两种方案的第一种) 2:当且仅当通过“另外一个点”的时候,有更短路径,即更新路径Path[v][w],把“另外一个点”放进Path[v][w]里 ** 路径输出 1:不防把起点(G.vexs[i]),和Path[i][j]先输出(Path[i][j]是路径的第一个点) 2:循环输出直到(Path[temp][j] == j)为止!!! ** 自己可能还没有讲清楚,不用担心我在画个图解释下 请参考:https://mp.csdn.net/mdeditor/80579845 ****************************************************************************************************************/
void ShortestPath_FLOYD(MGraph &G, shortestCode &Path)
{
    //最短路径长度DFloyd[][]
    int **DFloyd = (int **)malloc(G.vexnum * sizeof(int *));                    //动态二维数组DFloyd[][],分配G.vexnum行
    for (int i = 0; i < G.vexnum; ++i) {                                        //为每行分配G.vexnum列
        DFloyd[i] = (int *)malloc(G.vexnum * sizeof(int));
    }

    //最短路径Path[][]
    Path = (shortestCode)malloc(G.vexnum * sizeof(char *));                     //动态二维数组Path[][],分配G.vexnum行
    for (int i = 0; i < G.vexnum; ++i) {                                            //为每行分配G.vexnum列
        Path[i] = (char *)malloc(G.vexnum * sizeof(char));
    }

    for (int v = 0; v < G.vexnum; v++)
        for (int w = 0; w < G.vexnum; w++) {
            if (v == w)                                                         //这里把主对角线权值变为0,不然主对角线会错,因为之前赋值的∞,举个例子B->B,现有B->C + C->B之和肯定小于∞,所以主对角线发生错误
                G.arcs[v][w].wight = 0;
            DFloyd[v][w] = G.arcs[v][w].wight;                                  //把初始值存入DFloyd[][]
            Path[v][w] = G.vexs[w];                                             //矩阵Path的初值则为各个边的终点顶点
        }
    printf("Path矩阵:\n");
    for (int i = 0; i < G.vexnum; i++) {
        for (int j = 0; j < G.vexnum; j++)
            printf("%c\t", Path[i][j]);
        printf("\n");
    }
    /****************************************************************** ** u:经过另外一个点 ** v:起点 ** w:终点 *******************************************************************/
    for (int u = 0; u < G.vexnum; u++)
        for (int v = 0; v < G.vexnum; v++)
            for (int w = 0; w < G.vexnum; w++)
                if (DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w]) {               //比较两种方案,取更小的
                    DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w];
                    Path[v][w] = Path[v][u];                                    //更新Path矩阵
                }

    printf("\n\t\t弗洛伊德(Floyd)算法\nD矩阵:\n");
    for (int i = 0; i < G.vexnum; i++){
        for (int j = 0; j < G.vexnum; j++)          
            if (DFloyd[i][j] < INT__MAX)
                printf("%d\t", DFloyd[i][j]);
            else
                printf("∞\t");
        printf("\n");
    }

    printf("Path矩阵:\n");
    for (int i = 0; i < G.vexnum; i++) {
        for (int j = 0; j < G.vexnum; j++)
            printf("%c\t", Path[i][j]);
        printf("\n");
    }

    printf("\n弗洛伊德(Floyd)最短路径长度与最短路径如下所示:\n");
    /*************************************************************************************** ** 这里输出除自己到自己的所有路径 ** temp:通过Path[i][j] ---找到--> Path[Path[i][j]][j]<---判断是否相等-->j ****************************************************************************************/
    for (int i = 0; i < G.vexnum; i++)
    {
        int temp = 0;
        for (int j = 0; j < G.vexnum; j++) {
            if(DFloyd[i][j] != INT__MAX)
            {
                if (j != i)                                                         //这里不需要输出自己到自己的路径
                    printf("从顶点 %c ---> %c 最短路径长度为:%d\t最短路径为: %c %c", G.vexs[i], G.vexs[j], DFloyd[i][j], G.vexs[i], Path[i][j]);   //不防把起点(G.vexs[i]),和Path[i][j]先输出(Path[i][j]是路径的第一个点)
            }else if (DFloyd[i][j] == INT__MAX)
            {
                printf("从顶点 %c ---> %c 不可到达!!!", G.vexs[i], G.vexs[j]); //不存在最短路径
            }

            temp = LocateVex(G, Path[i][j]);
            while (temp != j) {                                                     //循环输出直到(Path[temp][j] == j)为止!!!
                printf(" %c", Path[temp][j]);                                       //输出最短路径
                temp = LocateVex(G, Path[temp][j]);
            }
            if (j != i)                                                             //自己到自己不需要换行
                printf("\n");                                                       
        }   
        printf("\n");                                                               //到某个点路径都显示了,换行
    }

    free(DFloyd);                                                                   //释放DFloyd[][]空间
    free(Path);                                                                     //释放Path[][]空间
}

程序输出

图5及输出:
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

图6及输出:
《最短路径--dijkstra算法、弗洛伊德(Floyd)算法(带路径输出)》

主函数

#include "stdafx.h"
#include "dijkatra.h"

int main()
{
    MGraph G;                                                                           //有向图的邻接矩阵
    shortestCode code;                                                                  //编码--输出路径

    printf("Please enter vexnum and arcnum: ");
    scanf("%d %d", &G.vexnum, &G.arcnum);                                               //输入结点数,弧数

    CreatMGraph(G);                                                                     //建立无向图的邻接矩阵 
    printf("\nTne output of Adjacency Matrix:\n\n");
    printMatrixGraph(G);                                                                //输出邻接矩阵

    ShortestPath_DIJ(G, G.vexs[0], code);                                               //迪杰斯特拉(Dijkstra)算法--求得最短路径长度与最短路径
    ShortestPath_FLOYD(G, code);                                                        //弗洛伊德(Floyd)算法--求得最短路径长度与最短路径
    return 0;
}

四:感谢与源代码(VS2017)

1:感谢一下波主文章对我的帮助,我相信他们的文章对你也会有帮助!!!
https://blog.csdn.net/qq_34374664/article/details/52261672
https://blog.csdn.net/txl199106/article/details/44980923
2:源代码(VS2017)
链接: https://pan.baidu.com/s/1J4WLSIfPt8s-Gc8IOmCw 密码: uwuw
3:如果你觉得本文还不错,请务必说明出处。

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/weixin_39956356/article/details/80620667
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞