Bellman-Ford算法
Bellman-Ford算法解决的是一般情况下的单源最短路径问题,在这里,边的权重可以为负值。给定带权重的有向图 G = ( V , E ) G=(V,E) G=(V,E)和权重函数 w : E − R w: E-R w:E−R,Bellman-Ford算法返回一个布尔值,以表明是否存在一个从源节点可以到达的权重为负值的环路,如果存在这样一个环路,算法将告诉我们不存在解决方案,如果没有这种环路存在,算法将给出最短路径和它们的权重。
Bellman-Ford算法实现步骤:
-
Step 1:从带有权重的图开始
-
Step 2:选择一个开始节点,并且给剩余其他节点路径距离赋值为无穷大
-
Step 3:访问每条边,进行松弛操作,更新节点距离
-
Step 4:需要操作 V V V次,因为在最坏情况下,一个节点的路径距离可能需要调整V次
- Step 5: 重复上面过程,最终从开始节点到每个节点的路径距离调整更新,获得最终结果
Bellman Ford算法的复杂度为 O ( V E ) O(VE) O(VE)
python实现如下:
# -*-coding:utf8-*-
import sys
class Graph:
def __init__(self, V):
self.V = V
self.graph = []
# add edges
def add_edge(self, s, d, w):
self.graph.append([s, d, w])
# print the solution
def print_solution(self, dist):
print(" Vertex Distance from Source")
for i in range(self.V):
print("{0}\t\t{1}".format(i, dist[i]))
def bellman_ford(self, src):
# step 1: fill the distance array and predecessor array
dist = [float("Inf")] * self.V
# Mark the source vertex
dist[src] = 0
# step 2 : relax edges |V|-1 times
for _ in range(self.V - 1):
for s, d, w in self.graph:
if dist[s]!= float("Inf") and dist[s] + w < dist[d]:
dist[d] = dist[s] + w
# step 3: detect negative cycle
for s, d, w in self.graph:
if dist[s] != float("Inf") and dist[s] + w < dist[d]:
print("Graph contains negative weight cycle")
return
# No negative weight cycle found, print the distance
self.print_solution(dist)
if __name__=='__main__':
g = Graph(5)
for s,d,w in [(0,1,4),(0,2,2),(1,2,3),(2,1,1),(1,3,2),(1,4,3),(2,4,5),(2,3,4),(4,3,-5)]:
g.add_edge(s,d,w)
g.bellman_ford(0)
Dijkstra算法
Dijkstra算法解决的是带权重的有向图上单源最短路径问题,该算法要求所有边的权重都为非负值。注意与最小生成树的区别,最小生成树需要包含图中的所有节点,而Dijkstra算法是找出任意两个节点的最短路径。
Dijkstra算法的实现步骤如下:
-
Step 1: 从一个带有权重的图开始
-
Step 2: 选择一个开始节点,并且对其它节点路径设置为无穷大
-
Step 3: 遍历每个节点,并且更新路径长度
-
Step 4: 节点的路径长度小于新的路径长度,则不更新
-
Step 5: 以及遍历过的节点不再更新路径长度
-
Step 6: 每一次迭代,选择没有遍历过的节点,并且选择路径最小的一个,所以下图节点5在节点7之前选择
-
Step 7:重复上面步骤,直到所有节点都遍历
Dijkstra算法时间复杂度为 O ( E l o g V ) O(ElogV) O(ElogV)
python实现如下:
# -*-coding:utf8 -*-
import sys
class Graph:
def __init__(self, vertices, edgs):
self.vertices= vertices
self.edgs = edgs
self.num_of_vertices = len(vertices[0])
self.visited_and_distance = [[0,0]]
def to_be_visited(self):
v = -1
for index in range(self.num_of_vertices):
if self.visited_and_distance[index][0] == 0 and (v < 0 or self.visited_and_distance[index][1] <= self.visited_and_distance[v][1]):
v = index
return v
def distance(self):
for i in range(self.num_of_vertices-1):
self.visited_and_distance.append([0, sys.maxsize])
for vertex in range(self.num_of_vertices):
# find next vertex to be visited
to_visit = self.to_be_visited()
for neighbor_index in range(self.num_of_vertices):
if self.vertices[to_visit][neighbor_index] ==1 and self.visited_and_distance[neighbor_index][0] == 0:
new_distance = self.visited_and_distance[to_visit][1] + edges[to_visit][neighbor_index]
if self.visited_and_distance[neighbor_index][1] > new_distance:
self.visited_and_distance[neighbor_index][1] = new_distance
self.visited_and_distance[to_visit][0] = 1
if __name__=='__main__':
vertices = [[0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 0, 0, 1, 0],
[1, 1, 0, 1, 1, 0, 0],
[1, 0, 1, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0, 1],
[0, 0, 0, 1, 0, 1, 0]]
edges = [[0, 0, 1, 2, 0, 0, 0],
[0, 0, 2, 0, 0, 3, 0],
[1, 2, 0, 1, 3, 0, 0],
[2, 0, 1, 0, 0, 0, 1],
[0, 0, 3, 0, 0, 2, 0],
[0, 3, 0, 0, 2, 0, 1],
[0, 0, 0, 1, 0, 1, 0]]
graph = Graph(vertices, edges)
graph.distance()
i = 0
for distance in graph.visited_and_distance:
print("Distance of ", chr(ord('a')+i), "from source vertex: ", distance[1])
i+=1