图论最短路径——Bellman-Ford Algorithm算法

这篇具有很好参考价值的文章主要介绍了图论最短路径——Bellman-Ford Algorithm算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

贝尔曼-福特算法:处理负权边的最短路径算法

在图论中,寻找最短路径是一个常见且重要的问题。对于这个问题,有许多算法可以解决,其中最著名的是 Dijkstra 算法。然而,当图中包含负权边时,Dijkstra 算法可能无法正确工作。这时,贝尔曼-福特(Bellman-Ford)算法就派上了用场。

贝尔曼-福特算法的基本原理

贝尔曼-福特算法可以在含有负权边的图中找到从单个源点到所有其他顶点的最短路径。这个算法的核心是反复“松弛”图中的所有边。所谓“松弛”,指的是更新当前找到的到各顶点的最短路径估计。

算法步骤

  1. 初始化:将所有顶点的最短路径估计值初始化为无穷大,只有源点的最短路径估计值设置为0。

  2. 边的松弛:对每条边进行松弛操作。如果当前顶点v到顶点u的最短路径估计加上从v到u的边的权重比当前顶点u的最短路径估计更小,那么就更新顶点u的最短路径估计。

  3. 重复执行:上述步骤在所有边上重复进行 |V|-1 次,其中 |V| 是顶点的数量。

  4. 检测负权环:最后,再次对所有边进行松弛操作,如果还能找到更短的路径,则说明图中存在负权环。

时间复杂度

贝尔曼-福特算法的时间复杂度为 O(VE),其中 V 是顶点数,E 是边数。这比 Dijkstra 算法的时间复杂度高,但它能处理更复杂的情况。

负权环检测

贝尔曼-福特算法的一个重要特性是能够检测图中是否存在负权回路。如果在完成标准的 |V|-1 次迭代后,再进行一次所有边的遍历,如果这时还能找到更短的路径,则说明图中存在负权回路。

代码实现

边的定义

struct Edge {
	int to;
	int weight;

	Edge(int t, int w) :to(t), weight(w) {}
};

用邻接表初始化一个图

	int n = 5;//顶点数
	vector<vector<Edge>>graph(n);
	graph[0].push_back(Edge(1, 10));
	graph[0].push_back(Edge(2, 5));
	graph[1].push_back(Edge(2, 2));
	graph[1].push_back(Edge(3, 1));
	graph[2].push_back(Edge(1, 3));
	graph[2].push_back(Edge(3, 9));
	graph[2].push_back(Edge(4, 2));
	graph[3].push_back(Edge(4, 4));
	graph[4].push_back(Edge(3, 6));
	graph[4].push_back(Edge(0, 7));

bellman算法实现

//graph是用邻接表表示的图
vector<int> bellman(const vector<vector<Edge>>& graph, const int& src) {
	int n = graph.size();
	//dis储存从出发节点到目标节点的最短距离,初始化为极大值
	vector<int> dis(n, INT_MAX);
	//出发节点到自身的距离为0
	dis[src] = 0;

	// 进行 n-1 次循环,如果某次循环时dis数组没有被更新则结束循环
	for (int k = 0; k < n - 1; k++) {
		//对每一个节点进行遍历
		for (int i = 0; i < n; i++) {
			//遍历每个节点的每条边
			for (const Edge& edge : graph[i]) {
				int u = i;
				int v = edge.to;
				int w = edge.weight;
				// 如果找到更短的路径则更新
				if (dis[u] < INT_MAX && dis[u] + w < dis[v]) {
					dis[v] = dis[u] + w;
				}
			}
		}
	}

	// 检测负权回路,更新dis数组时对图遍历了n-1次,如果第n次遍历仍然能找到更短的路径则说明图中存在负权回路
	for (int i = 0; i < n; i++) {
		for (const Edge& edge : graph[i]) {
			int u = i;
			int v = edge.to;
			int w = edge.weight;
			// 如果仍能找到更短的路径,说明存在负权回路
			if (dis[u] < INT_MAX && dis[u] + w < dis[v]) {
				cout << "图中存在负权回路" << endl;
				return vector<int>(); // 或者可以返回一个特殊值表示存在负权回路
			}
		}
	}

	return dis;
}

与Dijkstra算法的比较

#include <iostream>
#include <vector>
#include<queue>

using namespace std;

struct Edge {
	int to;
	int weight;

	Edge(int t, int w) :to(t), weight(w) {}
};

//graph是用邻接表表示的图
vector<int>dijkstra(const vector<vector<Edge>>& graph, int src) {
	int n = graph.size();
	//容器定义
		//储存各个顶点到src顶点的距离
	vector<int>dis(n, INT_MAX);
	//记录访问过的顶点	
	vector<bool>vis(n, false);
	//用优先级队列来处理距离最短的顶点,pair<int,int>的第一个int存储距离,第二个int存储顶点;底层用vector来存储这个队列;greater表示从小到大排
	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>pq;

	//src顶点到自己的距离为0
	dis[src] = 0;
	pq.push({ 0,src });

	while (!pq.empty()) {
		//v表示当前距离最短的顶点
		int v = pq.top().second; pq.pop();
		//若是访问过当前顶点则跳过
		if (vis[v])continue;
		vis[v] = true;
		//访问邻接顶点
		for (const auto& edge : graph[v]) {
			int t = edge.to;
			int w = edge.weight;

			if (!vis[t] && w + dis[v] < dis[t]) {
				dis[t] = w + dis[v];
				pq.push({ dis[t],t });
			}
		}

	}
	return dis;
}
//graph是用邻接表表示的图
vector<int> bellman(const vector<vector<Edge>>& graph, const int& src) {
	int n = graph.size();
	//dis储存从出发节点到目标节点的最短距离,初始化为极大值
	vector<int> dis(n, INT_MAX);
	//出发节点到自身的距离为0
	dis[src] = 0;

	// 进行 n-1 次循环,如果某次循环时dis数组没有被更新则结束循环
	for (int k = 0; k < n - 1; k++) {
		//对每一个节点进行遍历
		for (int i = 0; i < n; i++) {
			//遍历每个节点的每条边
			for (const Edge& edge : graph[i]) {
				int u = i;
				int v = edge.to;
				int w = edge.weight;
				// 如果找到更短的路径则更新
				if (dis[u] < INT_MAX && dis[u] + w < dis[v]) {
					dis[v] = dis[u] + w;
				}
			}
		}
	}

	// 检测负权回路,更新dis数组时对图遍历了n-1次,如果第n次遍历仍然能找到更短的路径则说明图中存在负权回路
	for (int i = 0; i < n; i++) {
		for (const Edge& edge : graph[i]) {
			int u = i;
			int v = edge.to;
			int w = edge.weight;
			// 如果仍能找到更短的路径,说明存在负权回路
			if (dis[u] < INT_MAX && dis[u] + w < dis[v]) {
				cout << "图中存在负权回路" << endl;
				return vector<int>(); // 或者可以返回一个特殊值表示存在负权回路
			}
		}
	}

	return dis;
}


int main() {
	int n = 5;//顶点数
	vector<vector<Edge>>graph(n);
	graph[0].push_back(Edge(1, 10));
	graph[0].push_back(Edge(2, 5));
	graph[1].push_back(Edge(2, 2));
	graph[1].push_back(Edge(3, 1));
	graph[2].push_back(Edge(1, 3));
	graph[2].push_back(Edge(3, 9));
	graph[2].push_back(Edge(4, 2));
	graph[3].push_back(Edge(4, 4));
	graph[4].push_back(Edge(3, 6));
	graph[4].push_back(Edge(0, 7));

	vector<int>shortest_path = dijkstra(graph, 0);
	vector<int>sp = bellman(graph, 0);
	cout << shortest_path[3] << endl;//输出9
	cout << sp[3] << endl;//输出9
	return 0;
}

bellman-ford algorithm,数据结构与算法,算法

 这个图中不存在负权路径,不能体现bellman算法的优势

bellman-ford algorithm,数据结构与算法,算法

对于这种带负权的图dijkstra算法就失效了

适用场景

由于其能够处理负权边的特性,贝尔曼-福特算法非常适合于需要处理复杂权重系统的场景,如经济系统、交通网络等领域的建模。

结语

尽管贝尔曼-福特算法在时间复杂度上不如某些其他算法,但其能力处理更为复杂的图形结构,特别是含有负权边的图,使其成为一个非常有用的工具。文章来源地址https://www.toymoban.com/news/detail-860133.html

到了这里,关于图论最短路径——Bellman-Ford Algorithm算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 最短路之 Bellman-ford 算法

    若有向图有n个点,m条边 。 扫描所有边,对每条边进行一次松弛(即对a,b为端点 , 权重为w的边,dist[b] = min(dist[a] , dist[a] + w )) 重复此流程(最多重复n次)直到没有更新操作发生 给你一张 n 个顶点 m 条边的有向简单图,顶点编号从 1 到 n,每条边都有一个边权,边权为非

    2024年02月17日
    浏览(30)
  • 最短路径算法( Dijkstra + Bellman-Ford + SPFA + Floyd)

       文章目录 一、Dijkstra 算法 1、1 朴素版Dijkstra算法 1、1、1 Dijkstra求最短路 I 1、1、2 题解关键思路与与解答 1、2 堆优化版Dijkstra算法 1、2、1 Dijkstra求最短路 II 1、2、2 题解关键思路与答案 二、Bellman-Ford 算法 2、1 Bellman-Ford算法求有边数限制的最短路 2、1、1 题目描述 2、

    2023年04月08日
    浏览(24)
  • Bellman-Ford-贝尔曼-福特-算法求最短路-负环

    Bellman-Ford(贝尔曼-福特)算法基于松弛操作的单源最短路算法。 e[u]存u点的出边的邻点和边权,d[u]存u点到源点的距离。 初始化,ds]=0,d[其它点]=+o; 执行多轮循环。每轮循环,对所有边都尝试进行一次松弛操作; 当一轮循环中没有成功的松弛操作时,算法停止 为什么最坏需要

    2024年02月13日
    浏览(27)
  • 算法基础复盘笔记Day06【搜索与图论】—— Dijkstra、bellman-ford、spfa、Floyd

    ❤ 作者主页:欢迎来到我的技术博客😎 ❀ 个人介绍:大家好,本人热衷于 Java后端开发 ,欢迎来交流学习哦!( ̄▽ ̄)~* 🍊 如果文章对您有帮助,记得 关注 、 点赞 、 收藏 、 评论 ⭐️⭐️⭐️ 📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉 1. 题目

    2023年04月22日
    浏览(35)
  • 最短路问题 Bellman-Ford(单源最短路径)(图解)

    对于边(u,v),用dist(u)和(u,v)的和尝试更新dist(v):                          dist(v) = min(dist(v) , dist(u)+l(u,v) 注:dist(i)为源点(起点)到i点的距离,l(u,v)为u-v的边权。 Bellman-Ford的基本操作是进行多次迭代,每一轮迭代对图上所有边进行松弛操作,直到

    2024年02月09日
    浏览(28)
  • 图论详解——Bellman-Ford(清晰易懂)

    开学第一周,晚上属实作业有点乱 于是就拖更了一周 今天我们来讲解一下图论最短路径算法中 最简单 最清晰易懂 同时时间复杂度最高的算法 它的时间复杂度能达到O(VE)(点的数量*边的数量) 在学习Bellman-Ford之前,你需要先学会链式前向星 大家可以上网或者其他途径自行

    2024年02月06日
    浏览(29)
  • 单源最短路径(spfa,Dijkstra, bellman-ford)

    目录  Dijkstra 原理:基于贪心。 为什么 Dijkstra 不能处理有负边的情况 Bellman-ford 原理:动态规划, 实质见floyd的另一篇博客 1,能找负环, 2,有变数限制的最短路径 spfa 原理 spfa怎么求负环, 原理:基于贪心。 第一步 初始化距离,dist[1] = 0, 一号点到起点的距离为0, 其他点

    2024年02月04日
    浏览(38)
  • Bellman-ford 贝尔曼-福特算法

    Bellman-ford算法可以解决负权图的单源最短路径问题 --- 它的优点是可以解决有负权边的单源最短路径问题, 而且可以判断是否负权回路 它也有明显的缺点,它的时间复杂度O(N*E)(N是点数 , E是边数)普遍是要高于Dijkstra算法O(N^2)的,像这里,我们使用邻接矩阵实现,那

    2024年02月06日
    浏览(29)
  • 图搜索算法详解 - DFS、BFS、Bellman-Ford、Dijkstra

    图搜索算法是许多应用程序的基础,例如社交网络分析、路径规划、数据挖掘和推荐系统。在本文中,我们将深入探讨图搜索算法的世界,探索它们的定义、重要性和实际应用。 图搜索算法是一种用于遍历图的技术,图是由 关系 连接的 节点集合 。在社交网络、网页或生物

    2024年02月16日
    浏览(31)
  • C++ | 数据结构与算法 | 单源最短路径 | Dijkstra && Bellman Ford

    (关于代码实现的图结构,可以看图结构的实现这篇文章) Dijkstra的实现与Prim的实现相似,两者都是通过贪心思想实现,它们有什么不同呢?首先Prim算法是针对无向图的最小生成树的,而Dijkstra算法是针对 有向图 的最短路径生成。一个的目的是连接图中的所有顶点,生成一

    2024年02月03日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包