9.4 最短路径算法

最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:

  • 确定起点的最短路径问题 - 即已知起始结点,求起始点到所有点的最短路径的问题。
  • 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,有向图中该问题等同于把所有路径方向反转的确定起点的问题。
  • 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
  • 全局最短路径问题 - 求图中所有的最短路径。

上面引自百度百科,最短路径的含义很好理解,下面我们直接看一下问题解法。

Dijkstra 迪杰斯特拉 

迪杰斯特拉算法,计算的是一个点到其余所有点的最短路径,或者说叫单源路径问题,对应于上述前三类问题。算法也是满满的贪心和动态规划的思路。
算法基本思想:

如果点 i 到点 j 的最短路径经过k,则ij路径中,i到k的那一段一定是i到k的最短路径。

核心思路:
(1)用3个一维数组(长度为图中顶点个数):一个用来标识该顶点是否已经找到最短路径,一个数组short_path用来记录初始点到该点的最短路径,还有一个记录最短路径情况下该点的前一个顶点是什么。
(2)求解:计算v0到vk的最短路径时,比较当前short_path[k]的值与 short_path[i] + vi-vk的距离相当于看一下绕路会不会更近。

Floyd 弗洛伊德算法

 

弗洛伊德与迪杰斯特拉的区别,就是弗洛伊德算法用于求所有点到所有点的最短距离。该算法非常优雅,我们学习一个。

算法核心思路其实和迪杰斯特拉一致,由于要求所有点到所有点,算法需要三次循环遍历,迪杰斯特拉记录路径的一维数组变成了二维数组。

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

贝尔曼-福特算法是由理查德·贝尔曼(Richard Bellman) 和 莱斯特·福特 创立的,求解单源最短路径问题的一种算法。有时候这种算法也被称为 Moore-Bellman-Ford 算法,因为 Edward F. Moore 也为这个算法的发展做出了贡献。它的原理是对图进行V-1次松弛操作,得到所有可能的最短路径。其优于迪杰斯特拉的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达O(VE)。


仍然以此图为例,我们演示一下迪杰斯特拉和佛洛依德算法。

已知条件还是顶点数和边:

n = 9
edge = [(0,1,10), (0,5,11), (1,2,18),(1,6,16), (1,8,12), (2,3,22),(2,8,8), (3,4,20), (3,6,24), (3,7,16), (3,8,21), (4,5,26), (4,7,7), (5,6,17), (6,7,19)]
def shortestPath_Dijkstra(n, edge, begin, end):
    from collections import defaultdict
    # 根据边 建图
    graph = defaultdict(dict)
    for e in edge:
        graph[e[0]][e[1]] = e[2]
        graph[e[1]][e[0]] = e[2]
    
    # 初始化关键变量
    short_path = [0 for i in range(n)]
    prenode = [0 for i in range(n)]
    visit = [0 for i in range(n)]
    for i in range(n):
        short_path[i] = graph[begin].get(i, 999)
    short_path[begin] = 0
    visit[begin] = 1
    
    # 核心部分
    for i in range(n-1):
        minw = 999
        node = begin
        # 找当前最近点
        for j in range(n):
            if not visit[j] and short_path[j] < minw:
                node = j
                minw = short_path[j]
        if node == end:
            break
        visit[node] = 1
        short_path[node] = 0
        # 更新short_path
        for k in range(n):
            if not visit[k] and (short_path[k] > minw + graph[node].get(k, 999)):
                short_path[k] = minw + graph[node].get(k, 999)
                prenode[k] = node
        if node == end:
            break
    route = [str(end)]
    x = end
    while x != begin:
        x = prenode[x]
        route.append(str(x))
    return short_path[end], "-".join(route[::-1])

注:如果求起始点到所有点的距离,把end相关内容去掉。

代码运行结果如下

shortestPath_Dijkstra(n, edge, 0, 4)
# (37, '0-5-4')
shortestPath_Dijkstra(n, edge, 0, 7)
# (44, '0-5-4-7')
def shortPath_Floyd(n, edge):
    from collections import defaultdict
    import numpy as np
    # 根据边 建图
    graph = defaultdict(dict)
    for e in edge:
        graph[e[0]][e[1]] = e[2]
        graph[e[1]][e[0]] = e[2]
    
    # 初始化关键变量
    short_path = np.zeros((n, n))
    prenode = np.zeros((n, n))
    
    for i in range(n):
        for j in range(n):
            short_path[i, j] = graph[i].get(j, 999)
            prenode[i, j] = j
    print("初始状态:")
    print(short_path)
    print(prenode)
    for i in range(n):
        for j in range(n):
            for k in range(n):
                if short_path[j, k] > short_path[j, i] + short_path[i, k]:
                    short_path[j ,k] = short_path[j, i] + short_path[i, k]
                    prenode[j ,k] = prenode[j ,i]
    print("最终结果:")
    print(short_path)
    print(prenode)

注意:该算法是求解所有点到所有点,所以最后结果是在两个矩阵中。

shortPath_Floyd(n, edge)
'''
初始状态:
[[999.  10. 999. 999. 999.  11. 999. 999. 999.]
 [ 10. 999.  18. 999. 999. 999.  16. 999.  12.]
 [999.  18. 999.  22. 999. 999. 999. 999.   8.]
 [999. 999.  22. 999.  20. 999.  24.  16.  21.]
 [999. 999. 999.  20. 999.  26. 999.   7. 999.]
 [ 11. 999. 999. 999.  26. 999.  17. 999. 999.]
 [999.  16. 999.  24. 999.  17. 999.  19. 999.]
 [999. 999. 999.  16.   7. 999.  19. 999. 999.]
 [999.  12.   8.  21. 999. 999. 999. 999. 999.]]
[[0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
 [0. 1. 2. 3. 4. 5. 6. 7. 8.]]
最终结果:
[[20. 10. 28. 43. 37. 11. 26. 44. 22.]
 [10. 20. 18. 33. 42. 21. 16. 35. 12.]
 [28. 18. 16. 22. 42. 39. 34. 38.  8.]
 [43. 33. 22. 32. 20. 41. 24. 16. 21.]
 [37. 42. 42. 20. 14. 26. 26.  7. 41.]
 [11. 21. 39. 41. 26. 22. 17. 33. 33.]
 [26. 16. 34. 24. 26. 17. 32. 19. 28.]
 [44. 35. 38. 16.  7. 33. 19. 14. 37.]
 [22. 12.  8. 21. 41. 33. 28. 37. 16.]]
[[1. 1. 1. 1. 5. 5. 1. 5. 1.]
 [0. 0. 2. 8. 6. 0. 6. 6. 8.]
 [1. 1. 8. 3. 3. 1. 1. 3. 8.]
 [8. 8. 2. 7. 4. 6. 6. 7. 8.]
 [5. 7. 3. 3. 7. 5. 7. 7. 3.]
 [0. 0. 0. 6. 4. 0. 6. 4. 0.]
 [1. 1. 1. 3. 7. 5. 1. 7. 1.]
 [4. 6. 3. 3. 4. 4. 6. 4. 3.]
 [1. 1. 2. 3. 3. 1. 1. 3. 2.]]

'''

Leetcode题目 —— 743. Network Delay Time

You are given a network of n nodes, labeled from 1 to n. You are also given times, a list of travel times as directed edges times[i] = (ui, vi, wi), where ui is the source node, vi is the target node, and wi is the time it takes for a signal to travel from source to target.

We will send a signal from a given node k. Return the time it takes for all the n nodes to receive the signal. If it is impossible for all the n nodes to receive the signal, return -1.

题解:

这道题是有向图中的最短路径问题,求点k到其他所有点的最短距离,取其极大值,乃网络的延迟时间。用上面的Dijkstra算法套用即可不再列出代码,多练习发现自己理解上的偏差。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值