算法系列——贝尔曼福特算法(Bellman-Ford)

本系列旨在用简单的人话讲解算法,尽可能避免晦涩的定义,读者可以短时间内理解算法原理及应用细节。我在努力!

本篇文章编程语言为Python,供参考。

贝尔曼福特算法(Bellman-Ford)

典型最短路径算法,用于计算一个节点到其他节点的最短路径。(Dijkstra算法也是)

 基本原理:逐遍的对图中每一个边去迭代计算起始点到其余各点的最短路径,执行N-1遍,最终得到起始点到其余各点的最短路径。(N为连通图结点数)

与迪杰斯特拉算法的区别:

        1. 迪杰斯特拉算法是借助贪心思想,每次选取一个未处理的最近的结点,去对与他相连接的边进行松弛操作;贝尔曼福特算法是直接对所有边进行N-1遍松弛操作。

        2. 迪杰斯特拉算法要求边的权值不能是负数;贝尔曼福特算法边的权值可以为负数,并可检测负权回路

名词解释:

1. 松弛操作:不断更新最短路径和前驱结点的操作。

2. 负权回路:绕一圈绕回来发现到自己的距离从0变成了负数,到各结点的距离无限制的降低,停不下来。

1. 邻接矩阵构建

基本用途:用一个二维数组存放两两结点之间的距离或权值。

2. 算法实现 

 3. 最短路径的寻找

同Dijkstra算法,准备一路径数组,更新时记录前驱结点。(较为简单,直接看也能看懂,不太懂可以翻阅之前Dijkstra算法那篇博客算法系列——迪杰斯特拉算法(Dijkstra)

 4. 优化

1. 提前跳出

2. 队列优化 

 见SPFA算法,见另一篇博客:算法系列——SPFA算法(贝尔曼-福特算法的队列优化形式)

附全部源码:

#北京 天津 郑州 济南 长沙 海南
# 0    1    2    3    4    5

#模拟从文件中读入图的各个路径
a = """
0 1 500
0 2 100
1 2 900
1 3 300
2 3 400
2 4 500
3 4 1300
3 5 1400
4 5 1500
"""

INF = float('inf')
N = 6


weight = []

def init():

    global weight
    
    #定义邻接矩阵 记录各城市之间的距离
    weight = [[INF if j!=i else 0 for j in range(N)] for i in range(N)]

    #解析数据
    b = [[int(i) for i in i.split(' ')] for i in a.split('\n') if i != '']

    for i in b:
        weight[i[0]][i[1]] = i[2]
        weight[i[1]][i[0]] = i[2]

init()

def bellman_ford(src, target):

    dist = [0 if i == src else INF for i in range(N)]

    #用于记录最后更新结点
    last_update = [src if i != INF else -1 for i in dist]  


    #松弛n-1次,因为最短路径的深度最多是n-1,n为结点数目
    for i in range(N-1):
        change = False
        #分别遍历边的两个顶点,从而实现遍历所有边。
        for j in range(N):
            for k in range(N):
                if dist[j] > dist[k] + weight[j][k]:
                    dist[j] = dist[k] + weight[j][k]
                    #记录更新该结点的结点编号
                    last_update[j] = k
                    #标记更改状态
                    change = True
                    
        #如果本轮未作出更改,说明已完成
        if not change:
            break

    #判断负权回路    
    for i in range(N):
        for j in range(N):
            if dist[j] > dist[i] + weight[j][i]:
                raise ValueError("存在负权回路")

    #输出从起点到终点的路径结点
    tmp = target
    path = []
    while tmp != src:
        path.append(tmp)
        tmp = last_update[tmp]
    path.append(src)
    print("->".join([str(i) for i in reversed(path)]))
    
    return dist[target]

后记:看了两个晚上,第一个晚上看着看着放弃了,今天晚上又鼓起勇气开写,用了一晚上的时间,现在是次日凌晨1:27,终于完成了,希望能对看完本文的各位有所帮助,有所启发吧。我也在写的过程中不断学习,不断变得更秃、变得更强,晚安。

本文大部分内容来自百度百科,掺杂了一部分个人的理解和思考,感谢完成该词条的大佬们,写的很详细很易懂。 贝尔曼-福特算法

  • 72
    点赞
  • 238
    收藏
    觉得还不错? 一键收藏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值