[算法与数据结构] - No.10 图论(3)- 最短路Dijkstra算法、Bellman-Ford算法和Floyd算法

2024-04-12 14:38

本文主要是介绍[算法与数据结构] - No.10 图论(3)- 最短路Dijkstra算法、Bellman-Ford算法和Floyd算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最短路径问题:如果从图中某一顶点(称为源点)到达另一顶点(称为终点)的路径可能不止一条,如何找到一条路径使得沿此路径上各边上的权值总和达到最小。
三种算法主要用途:

1.  边上权值非负情形的单源最短路径问题 —  Dijkstra算法 

2. 边上权值为任意值的单源最短路径问题 — Bellman和Ford算法

3. 所有顶点之间的最短路径 — Floyd算法

Dijkstra算法:贪心策略

算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,已求出最短路径的顶点集合S和其余未确定最短路径的顶点集合V-S,按最短路径长度的递增次序依次把V-S的顶点加入S中。选择V-S中距离远点路径最短的点v',以v'为中转点更新所有的点的最短路径

起始设置所有点距离源点v的最短路径。如果v'与v相连,那么最短路径就是<v,v'>的长度,否则就是无穷大


#include <iostream>using namespace std;
const int MAX =99999999;
unsigned int n,e;
int arr[100][100];
int visited[100] = {0};
int dis[100];
void Dijkstra()
{for(int i = 1; i<n; i++){int temp = MAX;int newVec = 0;for(int j = 1; j<n; j++){if(visited[j]==0&&dis[j]<temp){newVec = j;temp = dis[j];}}visited[newVec] = 1;for(int j = 1; j<n; j++){if(visited[j] == 0&&arr[newVec][j]<MAX){if(dis[j]>dis[newVec]+ arr[newVec][j]){dis[j] = dis[newVec]+ arr[newVec][j];}}}}
}
int main()
{cin>>n>>e;if(n>0&&e>0){for(int i=0; i<100; i++){for(int j=0; j<100; j++){if(i!=j)arr[i][j] = MAX;elsearr[i][j] = 0;}}for(int i=0; i<e; i++){int from,to,dis;cin>>from>>to>>dis;arr[from][to] = dis;//arr[to][from] = dis;}/*for(int i=0; i<n; i++){for(int j=0; j<n; j++){cout<<arr[i][j]<<" ";}cout<<endl<<endl;}*/for(int i = 0; i<n; i++){dis[i] = arr[0][i];}/*for(int i = 0; i<n; i++){cout<<distance[i]<<" ";}*/cout<<"Dijkstra 最短路:"<<endl;visited[0] = 1;Dijkstra();for(int i = 0; i<n; i++){cout<<dis[i]<<" ";}}return 0;
}
/*
5 7
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 60
*/

Bellman-Ford算法:持续松弛操作

什么叫松弛操作呢?在松弛一条边(u,v)的过程中,要测试是否可以通过u,对迄今找到的v的最短路径进行改进;如果可以改进的话,则更新d[v]。

bellman-ford算法是对图进行n-1次松弛操作,如果n-1次松弛操作以后还能继续进行松弛操作,那么说明原图中有负环(即圈的各边总权值为负)

为什么是n-1呢?我们可以确定的是,在一次对所有边进行松弛操作以后,我们至少可以确定一个定点的距离源点的最短路径(最坏可以考虑一条直线,中间有若干点,这种情况一次松弛只能确定一个点距离源点的最短路径。当一个点的连接的点个数超过一时,我们每次松弛确定得点个数大于1,最坏情况就是n-1次松弛确定所有点的最短路径)

那么,算法就很简单了,只有下面几行:

 for(int i = 0;i<n-1;i++){for(int j = 0;j<e;j++){if(distance[edge[j].to]>distance[edge[j].from]+edge[j].val){distance[edge[j].to] = distance[edge[j].from]+edge[j].val;}}}
完整代码如下:

#include <iostream>
#include <stdlib.h>
using namespace std;
const int MAX =99999999;
typedef struct Edge{int from,to;int val;
}Edge;
Edge edge[1000];
unsigned int n,e;
int dis[100];
bool Bellman()
{for(int i = 0;i<n-1;i++){for(int j = 0;j<e;j++){if(dis[edge[j].to]>dis[edge[j].from]+edge[j].val){dis[edge[j].to] = dis[edge[j].from]+edge[j].val;}}}bool flag = true;for(int k = 0 ; k < e;k++){if(dis[edge[k].to]>dis[edge[k].from]+edge[k].val){flag = false;break;}}return flag;
}
int main()
{cin>>n>>e;if(n>0&&e>0){for(int i = 1;i<n;i++){dis[i] = MAX;}dis[0] = 0;for(int i=0; i<e; i++){int from,to,dis;cin>>from>>to>>dis;edge[i].from = from;edge[i].to = to;edge[i].val = dis;//arr[to][from] = dis;}for(int i = 0; i<e; i++){if(edge[i].from == 0){dis[edge[i].to] = edge[i].val;}}for(int i = 0; i<n; i++){cout<<dis[i]<<" ";}cout<<"Bellman-Ford最短路:"<<endl;bool flag = Bellman();if(flag){for(int i = 0; i<n; i++){cout<<dis[i]<<" ";}}else{cout<<"存在负环"<<endl;}}return 0;
}/*
5 7
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 605 7
0 1 10
0 4 100
1 2 50
2 3 -2
3 0 -1
3 4 -6
4 2 -1
*/

Bellman的算法,存在一个问题就是每次都对所有的边进行进行松弛,会浪费一些时间复杂度。比如当我们还没有更新某个次序比较靠后的节点的时候,却会在第二个循环中,考虑用该节点去松弛其余节点。

比较好的改进措施是,我们使用一个队列,每次把刚刚进行松弛操作的节点加入队列中,每次从队列中取出节点去更新其他的节点这就是SPFA算法



void spfa()
{queue<int> myqueue;while(!myqueue.empty()){myqueue.pop();}myqueue.push(0);while(!myqueue.empty()){int v = myqueue.front();visited[v] = 0;myqueue.pop();for(int i = 0 ; i<n;i++){if(dis[i]>dis[v]+arr[v][i]){dis[i] = dis[v] + arr[v][i];if(visited[i]!=1){visited[i] = 1;myqueue.push(i);}}}}
}
使用上述spfa函数替代bellman函数即可

Floyd算法:计算图中每个顶点到其余所有顶点的最短路

这个算法时间复杂度为n^3。对于任意节点i,j遍历所有k,找到这样的k,使得以k为中转站的时候,i,j之间的最短路最短

一共三个for循环,算法很好记;前两个for循环用于遍历i,j后面一个for循环用于找到中转站k

核心代码:

    for(int k=0; k<n; k++)for(int i=0; i<n; i++)for(int j=0; j<n; j++)if(arr[i][k]<MAX && arr[k][j]<MAX && arr[i][j]>arr[i][k]+arr[k][j])arr[i][j]=arr[i][k]+arr[k][j];

算法实现:

#include <iostream>using namespace std;
const int MAX =99999999;
unsigned int n,e;
int arr[100][100];
void floyd()
{for(int k=0; k<n; k++)for(int i=0; i<n; i++)for(int j=0; j<n; j++)if(arr[i][k]<MAX && arr[k][j]<MAX && arr[i][j]>arr[i][k]+arr[k][j])arr[i][j]=arr[i][k]+arr[k][j];
}
int main()
{cin>>n>>e;if(n>0&&e>0){for(int i=0; i<100; i++){for(int j=0; j<100; j++){if(i!=j)arr[i][j] = MAX;elsearr[i][j] = 0;}}for(int i=0; i<e; i++){int from,to,dis;cin>>from>>to>>dis;arr[from][to] = dis;arr[to][from] = dis;}floyd();for(int i=0; i<n; i++){for(int j=0; j<n; j++){cout<<arr[i][j]<<" ";}cout<<endl;}return 0;}}/*
5 7
0 1 10
0 3 30
0 4 100
1 2 50
2 4 10
3 2 20
3 4 60
*/

文章中有的地方使用的是有向图表达,有的是无向图表达。望注意

P.S.文章不妥之处还望指正






这篇关于[算法与数据结构] - No.10 图论(3)- 最短路Dijkstra算法、Bellman-Ford算法和Floyd算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/897418

相关文章

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在

【数据结构】线性表:顺序表

文章目录 1. 线性表2. 顺序表2.1 概念及结构2.2 接口实现2.3 顺序表的问题及思考 1. 线性表 线性表是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式

数据结构9——排序

一、冒泡排序 冒泡排序(Bubble Sort),顾名思义,就是指越小的元素会经由交换慢慢“浮”到数列的顶端。 算法原理 从左到右,依次比较相邻的元素大小,更大的元素交换到右边;从第一组相邻元素比较到最后一组相邻元素,这一步结束最后一个元素必然是参与比较的元素中最大的元素;按照大的居右原则,重新从左到后比较,前一轮中得到的最后一个元素不参4与比较,得出新一轮的最大元素;按照上述规则,每一轮结

大林 PID 算法

Dahlin PID算法是一种用于控制和调节系统的比例积分延迟算法。以下是一个简单的C语言实现示例: #include <stdio.h>// DALIN PID 结构体定义typedef struct {float SetPoint; // 设定点float Proportion; // 比例float Integral; // 积分float Derivative; // 微分flo

LeetCode 算法:二叉树的中序遍历 c++

原题链接🔗:二叉树的中序遍历 难度:简单⭐️ 题目 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1] 输出:[1] 提示: 树中节点数目在范围 [0, 100] 内 -100 <= Node.

【Java算法】滑动窗口 下

​ ​    🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 🦌一.水果成篮 题目链接:904.水果成篮 ​ 算法原理 算法原理是使用“滑动窗口”(Sliding Window)策略,结合哈希表(Map)来高效地统计窗口内不同水果的种类数量。以下是详细分析: 初始化:创建一个空的哈希表 map 用来存储每种水果的数量,初始化左右指针 left

ROS2从入门到精通4-4:局部控制插件开发案例(以PID算法为例)

目录 0 专栏介绍1 控制插件编写模板1.1 构造控制插件类1.2 注册并导出插件1.3 编译与使用插件 2 基于PID的路径跟踪原理3 控制插件开发案例(PID算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习,掌握ROS2底层基本分布式原理,并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。 🚀详情:《ROS2从入门到精通》 1 控制插

算法与数据结构面试宝典——回溯算法详解(C#,C++)

文章目录 1. 回溯算法的定义及应用场景2. 回溯算法的基本思想3. 递推关系式与回溯算法的建立4. 状态转移方法5. 边界条件与结束条件6. 算法的具体实现过程7. 回溯算法在C#,C++中的实际应用案例C#示例C++示例 8. 总结回溯算法的主要特点与应用价值 回溯算法是一种通过尝试各种可能的组合来找到所有解的算法。这种算法通常用于解决组合问题,如排列、组合、棋盘游

【图像识别系统】昆虫识别Python+卷积神经网络算法+人工智能+深度学习+机器学习+TensorFlow+ResNet50

一、介绍 昆虫识别系统,使用Python作为主要开发语言。通过TensorFlow搭建ResNet50卷积神经网络算法(CNN)模型。通过对10种常见的昆虫图片数据集(‘蜜蜂’, ‘甲虫’, ‘蝴蝶’, ‘蝉’, ‘蜻蜓’, ‘蚱蜢’, ‘蛾’, ‘蝎子’, ‘蜗牛’, ‘蜘蛛’)进行训练,得到一个识别精度较高的H5格式模型文件,然后使用Django搭建Web网页端可视化操作界面,实现用户上传一