最短路径算法 | Bellman-Ford Algorithm

贝尔曼-福德算法是解决含有负权重边图的最短路径问题的有效方法,弥补了Dijkstra算法的不足。它通过重复|V|-1次松弛操作找到最短路径,并在最后一步检查是否存在负权重循环。算法时间复杂度为O(V*E),适用于分布式系统。本文详细介绍了算法步骤、Python实现及其实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在之前的文章中已经讨论了最短路径算法中最经典的Dijkstra‘s Algorithm。然而,Dijkstra's Algorithm虽然好用,却仍然存在一些缺点即无法解决带有负权重路线的问题,改进后的Dijkstra算法尽管可以解决一些简单的负权重问题,但仍然无法解决带有负循环的图的最短路径问题。

为了弥补Dijkstra's Algorithm的缺点,我们需要借用一个适用于任何图的最短路径算法,那就是我们今天要介绍的贝尔曼-福德算法。

Dijkstra 算法是一种贪心算法,时间复杂度为 O((V+E)LogV)(使用优先序列改进后)。Dijkstra 不适用于具有负权重的图,Bellman-Ford 适用于此类图,同时Bellman-Ford 也比 Dijkstra 更容易实现,并且非常适合分布式系统,缺点是 Bellman-Ford 的时间复杂度是 O(V * E) 

使用 Bellman-Ford Algorithm 查找到所有顶点的最短距离的步骤:

  • 首先,初始化从源点到所有顶点的距离为无穷,原点到原点本身的距离为 0,即创建大小为 |V| 的数组 dist[] 所有值都是无限的,除了 dist[src] ,其中 src 是源顶点。
  • 然后,计算最短路径,下面的操作重复执行 |V|-1 次,这里 |V| 是给定图中的顶点数,每次对每个边缘 uv 执行以下操作 
    • 如果 dist[v] > dist[u] + 边 uv 的权重,则更新 dist[v] 为dist[v] = dist[u] + 边 uv 的权重
  • 最后检查图中是否存在负权重循环。再次遍历每条边,对每条边uv执行以下操作:
    • 如果dist[v] > dist[u] + 边uv的权重,那么“图包含负权重循环” 
    • 第三步的思路是,第二步求出最短路径(如果图表不包含负权重循环)。如果我们再次遍历所有边并发现任何顶点存在更短的路径,则证明存在负权重循环。

Bellman-Ford Algorithm 的原理

与其他动态规划问题一样,该算法以自下而上的方式计算最短路径。它首先计算路径中最多有一条边的最短距离。然后,它计算最多有 2 条边的最短路径,依此类推。在外循环的第 i 次迭代之后,计算最多具有 i 条边的最短路径。可以有最大值 |V| – 任何简单路径中的 1 条边,这就是外循环运行 |v| 的原因 – 1 次。这个想法是,假设如果我们计算了最多具有 i 个边的最短路径,则没有负权重循环,那么对所有边的迭代保证给出最多具有 (i+1) 个边的最短路径。

算法图解

第 1 步:设0为起点。将所有距离初始化为无穷,除了0到自身的距离。图中的顶点总数为 5,因此每条边都必须处理 4 次。

第二步:所有边按如下顺序处理:(B, E), (D, B), (B, D), (A, B), (A, C), (D, C), ( B, C), (E, D)。当第一次处理所有边时,我们得到以下距离。第一行显示初始距离。第二行显示处理边缘 (B, E)、(D, B)、(B, D) 和 (A, B) 时的距离。第三行显示处理(A,C)时的距离。第四行显示处理 (D, C)、(B, C) 和 (E, D)时的距离。 

 第 3 步:第一次迭代计算出了最短路径只包涵一条边的距离。第二次遍历所有边时,我们得到以下距离(最后一行显示最终值)。

第 4 步:第二次遍历保证给出最多 2 条边的所有最短路径。注意,第二次遍历后所有距离已经最小化,因此第三次和第四次遍历不会更新距离。 

下面是Bellman-Ford算法的Python实现

class Graph:

	def __init__(self, vertices):
		self.V = vertices # No. of vertices
		self.graph = []

	# function to add an edge to graph
	def addEdge(self, u, v, w):
		self.graph.append([u, v, w])

	# utility function used to print the solution
	def printArr(self, dist):
		print("Vertex Distance from Source")
		for i in range(self.V):
			print("{0}\t\t{1}".format(i, dist[i]))

	# The main function that finds shortest distances from src to
	# all other vertices using Bellman-Ford algorithm. The function
	# also detects negative weight cycle
	def BellmanFord(self, src):

		# Step 1: Initialize distances from src to all other vertices
		# as INFINITE
		dist = [float("Inf")] * self.V
		dist[src] = 0

		# Step 2: Relax all edges |V| - 1 times. A simple shortest
		# path from src to any other vertex can have at-most |V| - 1
		# edges
		for _ in range(self.V - 1):
			# Update dist value and parent index of the adjacent vertices of
			# the picked vertex. Consider only those vertices which are still in
			# queue
			for u, v, w in self.graph:
				if dist[u] != float("Inf") and dist[u] + w < dist[v]:
					dist[v] = dist[u] + w

		# Step 3: check for negative-weight cycles. The above step
		# guarantees shortest distances if graph doesn't contain
		# negative weight cycle. If we get a shorter path, then there
		# is a cycle.

		for u, v, w in self.graph:
			if dist[u] != float("Inf") and dist[u] + w < dist[v]:
				print("Graph contains negative weight cycle")
				return

		# print all distance
		self.printArr(dist)


# Driver's code
if __name__ == '__main__':
	g = Graph(5)
	g.addEdge(0, 1, -1)
	g.addEdge(0, 2, 4)
	g.addEdge(1, 2, 3)
	g.addEdge(1, 3, 2)
	g.addEdge(1, 4, 2)
	g.addEdge(3, 2, 5)
	g.addEdge(3, 1, 1)
	g.addEdge(4, 3, -3)

	# function call
	g.BellmanFord(0)


 其他语言实现下载链接:

(包含各种语言:C、Python、Java、C++、C#、Javascript等均有示例)

免费​资源下载:Bellman-Ford Algorithm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值