C++算法:多源最短路径(Floyd)

news2024/11/24 11:11:21

文章目录

  • 前言
  • 一、Floyd算法
  • 二、代码实现
  • 总结


前言

前文单源最短路径Dijkstra中我们讨论了如何解决有向无环图的最短路径问题,Dijkstra只能解决一个起始点的问题,如果要解决每个顶点到任一顶点的最短路径呢?一个方法就是再循环一次,以每个顶点作为起点就可以了嘛,虽然不可避免的会有部分重复工作,但确实能解决,就是代码更复杂了。有没有一个优雅点的办法呢?今天要介绍的算法Floyd(弗洛伊德,好熟悉的名字~)就是一个优雅的解决办法。它与用Dijkstra循环每个顶点的时间复杂度差不多O(n3),但是代码极简。


一、Floyd算法

Floyd的思想就是前文提到的最优子结构。Floyd算法利用了动态规划的思想。设有向图G=(V,E),V是定点集,取某个顶点k。考虑顶点的一个子集{1,2,3, … k}。对任意一对顶点i,j∈V, 考察从i到j且中间顶点皆属于集合{1,2,3, … k}的所有的路径,设p是其中的一条最短路径 (p没有回路)。那么我们考虑两种情况:
(1) 如果k不是路径p的中间顶点,则P的所有中间顶点皆在集合{1, 2, 3, … k}中。因此,从顶点i到顶点j且满足中间顶点全在集合{1, 2, 3, … k}中的一条最短路径同样是从顶点i到顶点j且满足中间顶点均在集合 {1, 2, 3, … k} 中的一条 最短路径。这一点可以帮我们不停地缩小集合的范围。
(2)如果k是路径P的中间顶点,那么可将p分解为子路径p1<i,k>和子路径p2<k,j>,我们知道,最短路径的子路径也是最短路径,所以子路径p1和子路径p2分别是顶点i到k和顶点k到j且满足所有中间顶点均在集合{1,2,3, … k}上的最短路径。
如果我们用(dij n) 表示从顶点i到顶点j,且满足所有中间顶点属于集合{1,2,3, … k}的一条最短路径的权值和,那么上面两点就可以抽象成如下的递归式:

其中wij表示图中顶点i到j之间的边的权值,如果无边直接相连,则为+∞。k=0的时候,说明顶点i和顶点j之间没有中间点。由于是对任意路径,所以当k=n的时候,该图的每对顶点的最短路径的权值就可由矩阵 Dn = (dij n) 表示。
上面的递归式就是Floyd算法的核心思想,它说明Floyd算法是从全局的观点出发来找任意两个顶点间的最短路径。每取一个k值,都会遍历一次整个图矩阵,找出中间顶点在{1,2,3, … k}中所有的最短路径,随着k值的增加,算法的结果逐渐趋近于最终结果,当k=n时,就是我们需要的结果。这里大家要有一个意识,就是直接由边相连的顶点之间k=0的路径权值不一定小于经过多个点之后的路径的权值。

有了上述理论思想,我们继续用前文使用过的图来看看:

二、代码实现

我们继续在前文的代码改巴改巴,原矩阵就注释了。用一个path矩阵来代替,因为这个矩阵会被改写。再用一个二维数组pre来存储前面理论中讨论的 k,因为这个值和 i、j 有关,所以要用二维数组。

代码如下(示例):

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

class Graph{
    private:
        int vertex;      //顶点数
        //int** matrix;    //有向图关系矩阵
        int** path;    //存储关系矩阵
        int** pre;         //存储中间节点k

    public:
        const int maxium = 10000;                //最大值,表示不存在的边
        Graph(const int edges, const int nodes, int arr[][3]){
            vertex = nodes;  
            //matrix = new int* [vertex];          //生成有向图关系矩阵
            pre = new int* [vertex];
            path = new int* [vertex];             //生成有向图关系矩阵
            for (int i=0; i<vertex; ++i){
                pre[i] = new int[vertex];
                path[i] = new int[vertex];
                //matrix[i] = new int[vertex];
                for (int j=0; j<vertex; j++){
                    //matrix[i][j] = maxium;
                    path[i][j] = maxium;
                    pre[i][j] = -1;
                }
            }
            for (int i=0; i<edges; ++i){                    //生成有向图关系,maxium为不连接
                //matrix[arr[i][0]][arr[i][1]] = arr[i][2];
                path[arr[i][0]][arr[i][1]] = arr[i][2];
            }
        }

        ~Graph(){
            delete[] path;
            //delete[] matrix;
            delete[] pre;
        }

        void floyd(int s, int end){
            for (int k=0; k<vertex; ++k){
                for (int i=0; i<vertex; ++i){
                    for (int j=0; j<vertex; ++j){
                        if (path[i][k] + path[k][j] < path[i][j]){
                            path[i][j] = path[i][k] + path[k][j];
                            pre[i][j] = k;
                        }
                    }
                }
            }    
            show(s, end);
        }

        void show(int start, int end){  //显示路径,单源,多源直接for循环调用这个函数
            int k = pre[start][end];
            if (k != -1){
                show(start, k);
                cout << k << " ";
                show(k, end);
            }
        }
};

二维数组path,用于存储各顶点对的权值,即从i到j的最短路径权值。在Floyd函数中,通过三重循环遍历所有顶点对,更新path数组中的值。同时,还有一个二维数组pre,用于存储中间节点k。在Floyd函数中,当发现从i到j经过k比直接从i到j更短时,就更新path[i][j]和pre[i][j]的值。最后,通过show函数输出从起点到终点的最短路径。Floyd方法已经计算了所有顶点对之间的最短路径,如果要查看这个路径,循环调用show方法即可。


总结

Floyd算法的时间复杂度是O(n3),但是实现非常简单优雅,核心代码才几行。用示例图数据测试:

int main(){
    int arr[][3] = {{0,1,8},{0,3,16,},{0,4,7},{1,3,9},{1,5,5},{2,9,2},
                   {3,2,1},{3,6,10},{3,8,12},{4,7,5},{4,3,9},{4,8,7},{5,3,2},
                   {5,2,11},{6,2,13},{6,9,2},{7,6,8},{8,7,1},{8,6,6}};

    Graph t(19, 10, arr);
    t.floyd(0, 9);
    return 0;
}

结果是:1 5 3 2 ,这里偷懒没有写起点和终点,结果和Dijksrta算法得到的一致。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/667368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASEMI代理光宝高速光耦LTV-60L规格,LTV-60L封装

编辑-Z LTV-60L参数描述&#xff1a; 型号&#xff1a;LTV-60L 封装&#xff1a;LSOP-6 储存温度TST&#xff1a;-55~125℃ 工作温度TA&#xff1a;-40~105℃ 隔离电压VISO&#xff1a;5000VRMS 电源电压VCC&#xff1a;7V 平均正向输入电流IF&#xff1a;20mA 输入功…

电脑滤镜软件哪个好 滤镜调色有什么技巧

无论是图片还是视频&#xff0c;滤镜的作用都很大&#xff0c;通过滤镜改变色彩&#xff0c;能让图片或视频增加故事性和美感。电脑滤镜软件哪个好, 电脑滤镜软件有很多&#xff0c;这里我们主要介绍几款视频滤镜软件。滤镜调色有什么技巧&#xff1f;有模板的&#xff0c;我们…

api-hook,更轻量的接口测试工具

目录 前言 现状 api-hook优势 研发介绍 最后 前言 api-hook是一种轻量级的接口测试工具&#xff0c;它基于Python语言编写&#xff0c;可以通过配置文件定义接口测试用例&#xff0c;支持HTTP和HTTPS协议&#xff0c;并提供了灵活的校验机制和数据处理方式。 在网站的开发过…

Tailwindcss 入门

Tailwindcss 是一个功能类优先的 CSS 框架&#xff0c;通过 flex, pt-4, text-center 和 rotate-90 这种原子类组合快速构建网站&#xff0c;而不需要离开你的 HTML。就是记住原子类&#xff0c;不要再自己想 CSS 命名一股脑子写 HTMl 就行了&#xff01; 它与常规的 Bootstra…

佩戴舒适度极好的蓝牙耳机推荐,久戴不累的蓝牙耳机推荐

无论是在日常还是运动的场景下&#xff0c;我们通常都会选择佩戴着耳机&#xff0c;让我们能够释放压力&#xff0c;缓解枯燥的生活。可是&#xff0c;随着市面上的蓝牙耳机层出不穷、各种各样&#xff0c;导致很多小伙伴不知如何选购耳机了&#xff0c;下面我来给大家分享几款…

循踪讲述:成都的夜,有一盏灯为你亮

成都的夜晚总是这样&#xff0c;热闹而梦幻。霓虹灯下的酒吧一条街&#xff0c;你看着手里冒着蒸汽的小龙虾&#xff0c;却心里想着那个人。那个曾经一起在脚手架上挥汗如雨&#xff0c;携手走过平淡岁月&#xff0c;然后在灯红酒绿的生活里逐渐远离的人。 他&#xff0c;一个手…

【数据治理-06】做好数据分类分级,为数据安全有序流动保驾护航

我们常说人以类聚&#xff0c;物以群分&#xff0c;确实是这样&#xff0c;杜威说过“所有知识都是分类”&#xff01;很好理解&#xff0c;分类是认知经济&#xff0c;任何有效分类&#xff0c;都可以极大地节省我们的认知精力。数据分类分级具体说来&#xff0c;其实包含了2个…

欢迎来到 VOXEL WARS!

Sandbox Streams 的全新节目&#xff0c;我们希望你们能参与其中&#xff01; 我们正在寻找 15 名 Voxedit 艺术家&#xff0c;他们将需要抽出 1 小时进行现场表演&#xff08;仅限屏幕共享&#xff09;&#xff0c;并在节奏快速的环境中进行创作&#xff0c;以赢得“最佳快速设…

报道 | 7月国际运筹优化会议汇总

七月召开会议汇总&#xff1a; 30th International Annual EurOMA Conference Location:Leuven Important dates: Conference: July 3, 2023 - July 5, 2023 Details:https://euroma2023.org/ The Equilibrium Computation Workshop at EC Location:Kings College London…

STM32速成笔记—ADC

文章目录 一、什么是ADC二、ADC的用途三、STM32F103ZET6的ADC3.1 ADC通道对应引脚3.2ADC时钟3.3 ADC工作模式3.4 ADC转换时间3.5 ADC校准3.6 ADC转换结果与实际电压的换算 四、ADC配置步骤五、ADC配置程序5.1 ADC初始化程序5.2 软件触发AD转换5.3 读取AD转换结果 六、实战项目6…

运动健身APP开发需要具备哪些功能?

想要开发一款运动健身APP软件&#xff0c;需要具备哪些功能呢&#xff1f; 1、用户注册和登录&#xff1a;用户通过个人信息注册健身APP&#xff0c;登陆之后建立个人账号&#xff0c;以后使用秩序登录自己的账号就可以记录和追踪自己的健身计划和成果。 2、个人…

【新星计划回顾】第六篇学习计划-通过自定义函数和存储过程模拟MD5数据

&#x1f3c6;&#x1f3c6;时间过的真快&#xff0c;这是导师回顾新星计划学习的第六篇文章&#xff01; 最近这段时间非常忙&#xff0c;虽然导师首次参与新星计划活动已经在4月16日圆满结束&#xff0c;早想腾出时间来好好整理活动期间分享的知识点。 &#x1f3c6;&#x1…

机器学习:问题构建及框架化

机器学习作为一种解决方案&#xff0c;并不是“万金油”&#xff0c;它只适用于一些特定的场景。在实际应用中&#xff0c;我们首先需要进行问题构建——即通过分析问题以隔离需要解决的各个元素的过程。问题构建有助于确定项目的技术可行性&#xff0c;并提供一组明确的目标和…

应届生软件面试自我介绍(合集)

应届生软件面试自我介绍篇【1】 尊敬的领导&#xff1a; 您好! 我是中南大学(原中南工业大学)冶金科学与工程学院2014年轻金属冶金专业应届毕业生。在此临近毕业之际&#xff0c;我希望能得到贵单位的赏识与栽培。为了发挥自己的才能&#xff0c;特向贵单位自荐。自我介绍 中南…

在元宇宙上做传统建筑施工培训提高培训安全性和效果

随着VR虚拟现实技术不断发展&#xff0c;VR元宇宙在建筑行业中的应用也越来越广泛。通过VR元宇宙技术打通虚拟空间和现实空间实现建筑全生命周期的改进和优化&#xff0c;形成全新的数字建造体系&#xff0c;达到降低成本、提高生产效率、高效协同的效果&#xff0c;促进建筑业…

SignalR 跨域问题(Vue3+Net6)

文章目录 背景困难~~*调用 UseCors*~~ 解决办法环境错误信息解决方式部分代码 问题分析 背景 使用前后端分离技术&#xff0c;前端使用Vue&#xff0c;部署在独立的服务器上&#xff0c;后端接口部署在另外一个服务器上。 困难 网上找了一个多小时的SignalR的跨域问题&#…

textract OCR的安装使用

安装 pip install textract使用 在 Python 中&#xff0c;textract 是一个用于提取文本和信息的库。它提供了一个函数 textract.process()&#xff0c;用于处理不同类型的文档并提取文本内容。下面是 textract.process() 函数的各个参数的介绍&#xff1a; filename&#xf…

spring boot框架步骤

目录 1. 创建一个新的Spring Boot项目2. 添加所需的依赖3. 编写应用程序代码4. 配置应用程序5. 运行应用程序6. 编写和运行测试7. 部署应用程序 总结 当使用Spring Boot框架开发应用程序时&#xff0c;以下是一些详细的步骤&#xff1a; 1. 创建一个新的Spring Boot项目 使用…

ChatGPT在物流与运输行业的智能场景:智能调度和自动驾驶的前瞻应用

第一章&#xff1a;引言 随着人工智能技术的飞速发展&#xff0c;物流与运输行业正迎来一场革命。传统的调度和运输模式已经无法满足快速增长的物流需求和客户期望。在这一领域&#xff0c;ChatGPT作为一种先进的自然语言处理模型&#xff0c;具有巨大的潜力。本文将探讨ChatG…

【经验贴】多项目并行,如何解决资源管理这个难点?

随着公司业务的逐步增加&#xff0c;我手上管理的项目也多了起来&#xff0c;开始接触了一些中大型项目。但还没来得及算能拿到多少项目奖金&#xff0c;我就被接踵而至的管理难题压得喘不过气来&#xff0c;第一次感受到多项目并行带来的手忙脚乱的感觉。 我首先遇到了各种资…