最短路径问题-Floyd算法

具体步骤和图解看这个:https://www.cnblogs.com/ssyfj/p/9495960.html

一看这个就懂了-傻子也能看懂的弗洛伊德算法https://www.cnblogs.com/wangyuliang/p/9216365.html

复制代码
知识点:

1)Floyd算法适用于APSP(All Pairs Shortest Paths,多源最短路径),是一种动态规划算法,稠密图效果最佳。

2)时间复杂度比较高O(n*3),不适合计算大量数据。

3)可以算出任意两个节点之间的最短距离

4)无论是迪杰斯特拉算法还是弗洛伊德算法,对于有向图,无向图都是可以使用的。另外我们的最短路径一般都是针对有环图,无环图使用拓扑排序可以获得

5)弗洛伊德算法是用来求所有顶点到所有顶点的时间复杂度。

6)Floyd(
Floyd-Warshall)算法边权可正可负,但是不能解决带有“负权回路”(或者叫“负权环”)的图,因为带有“负权回路”的图没有最短路。
7)其实如果一个图中带有“负权回路”那么这个图则没有最短路。
(如果存在一个环(从某个点出发又回到自己的路径),而且这个环上所有权值之和是负数,那这就是一个负权环,也叫负权回路)
如下图:不存在1号顶点到3号顶点的最短路径。因为1->2->3->1->2->3->...->1->2->3这样路径中,
每绕一次1->-2>3这样的环,最短路就会减少1,永远找不到最短路。
复制代码

1:Floyd算法过程矩阵的计算----十字交叉法:

方法:两条线,从左上角开始计算一直到右下角 如下所示

给出矩阵,其中矩阵A是邻接矩阵,而矩阵Path记录u,v两点之间最短路径所必须经过的点

 

相应计算方法如下:

最后A3即为所求结果

2:实现代码

1)弗洛伊德算法定义了两个二维矩阵:
矩阵D记录顶点间的最小路径 ;例如D[0][3]= 10,说明顶点0 到 3 的最短路径为10。
矩阵P记录顶点间最小路径中的中转点 ;例如P[0][3]= 1 说明,0 到 3的最短路径轨迹为:0 -> 1 -> 3。
它通过3重循环,k为中转点,v为起点,w为终点,循环比较D0[v][w] 和 D0[v][k] + D0[k][w] 最小值,如果D0[v][k] + D0[k][w] 为更小值,
则把D0[v][k] + D0[k][w] 覆盖保存在D1[v][w]中。
2)核心思想:D1[v][w] = min{D0[v][k] + D0[k][w],D0[v][w]}【D0代表原来未更新前的数据,D1表示我们修改更新后的新的数据】
复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "queue.h"

#define MAXVEX 100    //最大顶点数
#define INFINITY 65535    //用0表示∞

typedef char VertexType;    //顶点类型,字符型A,B,C,D...
typedef int EdgeType;    //边上权值类型10,15,...

//邻接矩阵结构
typedef struct
{
    VertexType vers[MAXVEX];    //顶点表
    EdgeType arc[MAXVEX][MAXVEX];    //邻接矩阵,可看作边表
    int numVertexes, numEdges;    //图中当前的顶点数和边数
}MGraph;

typedef int Dist[MAXVEX][MAXVEX];    //存放各个顶点到其余顶点的最短路径权值和
typedef int Path[MAXVEX][MAXVEX];    //存放各个顶点到其余顶点前驱顶点位置

//创建邻接矩阵
void CreateMGraph(MGraph* G);
//显示邻接矩阵
void showGraph(MGraph G);

void Floyd(MGraph G,Path* path,Dist* dist);
void ShowDistAndPath(Path P, Dist D,int n);

void Floyd(MGraph G, Path* path, Dist* dist)
{
    int i,j,k;

    //初始化path和dist
    for (i = 0; i < G.numVertexes;i++)
    {
        for (j = 0; j < G.numVertexes;j++)
        {
            (*dist)[i][j] = G.arc[i][j];
            (*path)[i][j] = j;    //初始化为这个的一个好处就是自己到自己的路径就是自己,我们不用修改
        }
    }

    //使用弗洛伊德核心算法,三层循环求解
    for (k = 0; k < G.numVertexes;k++)
    {
        for (i = 0; i < G.numVertexes;i++)
        {
            for (j = 0; j < G.numVertexes;j++)
            {
                if ((*dist)[i][j]>((*dist)[i][k]+(*dist)[k][j])&&i!=j)
                {
                    //将权值和更新,路径也变为中转点
                    (*dist)[i][j] = (*dist)[i][k] + (*dist)[k][j];
                    (*path)[i][j] = (*path)[i][k];
                }
            }
        }
    }
}

void ShowDistAndPath(Path P, Dist D,int n)
{
    int i, j;
    printf("Printf Dist:\n");
    for (i = 0; i < n;i++)
    {
        for (j = 0; j < n; j++)
        {
            if (i==j)
                printf("    0");    //需要将我们的无穷转换一下再显示
            else
                printf("%5d", D[i][j]);
        }
        printf("\n");
    }

    printf("Printf Path:\n");
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
            printf("%5d", P[i][j]);
        printf("\n");
    }
}

int main()
{
    MGraph MG;
    CreateMGraph(&MG);
    showGraph(MG);
    Path path;
    Dist dist;
    Floyd(MG, &path, &dist);
    ShowDistAndPath(path, dist, MG.numVertexes);
    system("pause");
    return 0;
}

//生成邻接矩阵
void CreateMGraph(MGraph* G)
{
    int i, j, k, w;
    G->numVertexes = 9;
    G->numEdges = 16;
    //读入顶点信息
    G->vers[0] = 'A';
    G->vers[1] = 'B';
    G->vers[2] = 'C';
    G->vers[3] = 'D';
    G->vers[4] = 'E';
    G->vers[5] = 'F';
    G->vers[6] = 'G';
    G->vers[7] = 'H';
    G->vers[8] = 'I';

    //getchar();    //可以获取回车符
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = INFINITY;    //邻接矩阵初始化

    //创建了有向邻接矩阵
    G->arc[0][1] = 1;
    G->arc[0][2] = 5;
    G->arc[1][2] = 3;
    G->arc[1][3] = 7;
    G->arc[1][4] = 5;
    G->arc[2][4] = 1;
    G->arc[2][5] = 7;
    G->arc[3][4] = 2;
    G->arc[3][6] = 3;
    G->arc[4][5] = 3;
    G->arc[4][6] = 6;
    G->arc[4][7] = 9;
    G->arc[5][7] = 5;
    G->arc[6][7] = 2;
    G->arc[6][8] = 7;
    G->arc[7][8] = 4;

    for (i = 0; i < G->numVertexes;i++)
        for (k = i; k < G->numVertexes;k++)
            G->arc[k][i] = G->arc[i][k];
}


//显示邻接矩阵
void showGraph(MGraph G)
{
    for (int i = 0; i < G.numVertexes; i++)
    {
        for (int j = 0; j < G.numVertexes; j++)
        {
            if (G.arc[i][j] != INFINITY)
                printf("%5d", G.arc[i][j]);
            else
                printf("    0");
        }
        printf("\n");
    }
}
复制代码

 

posted on   Y-flower  阅读(885)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示