算法学习(六)—— 狄克斯特拉算法

  • Post author:
  • Post category:其他




系列文章目录


第一章:二分查找及大O表示法


第二章:选择排序


第三章:递归和快速排序


第四章:散列表


第五章:广度优先搜索

第六章:狄克斯特拉算法





前言


积累算法,记录学习




一、加权图

上一章介绍的图是没有权重的,即每一条边的意义一样,这样并不全面,因此引出一种加权图,这种图通常伴随的问题是

最快路径问题

比如对应下面这个图,假如使用广度优先算法时,找到的最短路径可能是“起点——A——终点”或者“起点——B——终点”,但是我们可以看出,在图上最快的路径并不是上述两条!因此广度优先搜索算法失效。对于这样的加权图,我们的狄克斯特拉算法就派上了用场。

在这里插入图片描述



二、算法原理

我们不用那种索然无味的算法流程,我们还是以上图为例,我们的目的是找到从起点到终点的最快路径,我们分以下步骤进行:

1)

找出最近的节点。

我们从起点出发,有两条路能走,前往A点和前往B点,最终目的是终点。并且我们可以知道前往A点用6分钟,前往B点用2分钟,不知道前往终点所需时间,因此设为无穷。所以来说,我们选择B节点这条路,因为它是最快的!拿小本子记下来:

在这里插入图片描述

2)

计算经B节点前往其各个邻居的所需时间。

现在我们站在B处,能够到达的地方有A和终点,所以从B到A的时间是3分钟,从B到终点的时间是5分钟。由此我们需要更新一下自己的小本子了。因为现在从起点到A点的最快时间是5分钟,到终点的最快时间是7分钟:

在这里插入图片描述

3)重复!我们现在再站在A点,由于A点到终点的时间是1分钟,因此又要更新一下小本子,因为现在从起点途径B再途径A再到终点才是我们的最快路径!仅需6分钟,显然这不是我们最短路径。

在这里插入图片描述

这些路上的时间在权重图的术语中叫

权重

这里需要注意一点,狄克斯特拉算法不能用在包含带有负权重的图中,因为对于处理过的节点,算法认为没有前往该节点的更短路径,这时我们就需要一种新的算法——

贝尔曼-福德算法

,这里暂时先不做详解,感兴趣的小伙伴可以自行学习。



三、算法实现

我们就实现上面这个图的狄克斯特拉算法,使用的数据结构也是散列表:它真是用处多多呢!

1、首先我们需要有存储图的散列表graph,将图上所有的信息表现在散列表中,这就是我们第一步要做的事:

>>> graph = {}
>>> graph["start"] = {}
>>> graph["start"]["a"] = 6
>>> graph["start"]["b"] = 2
>>> graph["a"] = {}
>>> graph["a"]["end"] = 1
>>> graph["b"] = {}
>>> graph["b"]["a"] = 3
>>> graph["b"]["end"] = 5
>>> graph["end"] = {}
>>> graph
{'start': {'a': 6, 'b': 2}, 'a': {'end': 1}, 'b': {'a': 3, 'end': 5}, 'end': {}}

2、然后有需要记录花费时间的散列表costs,这个是要更新的,所以我们先给他一个初始状态就行,就是站在起点我们所获知的时间花费,无穷用

infinity = float("inf")

表示

>>> infinity = float("inf")
>>> costs = {}
>>> costs["a"] = 6
>>> costs["b"] = 2
>>> costs["end"] = infinity
>>> costs
{'a': 6, 'b': 2, 'end': inf}

3、因为要记住路径,我们也就需要散列表来记住父节点,用parents来表示,当我们在起点时,a的父节点是起点,b的父节点也是起点,前往终点的父节点暂时未知为None。

>>> parents = {}
>>> parents["a"] = "start"
>>> parents["b"] = "start"
>>> parents["end"] = None

4、为了避免有双向图的可能性,我们需要记录一下处理过的点,避免陷入循环,所以要设置一个


processed = []

下面编写寻找最近邻居的程序:

def find_lowest_cost_node(costs):
    lowest_cost = float("inf")
    lowest_cost_node = None
    for node in costs:
        cost = costs[node]
        if cost < lowest_cost and node not in processed:
            lowest_cost = cost
            lowest_cost_node = node
    return lowest_cost_node

狄克斯特拉算法:

def dkstl(costs):
    fastest_path = []
    node = find_lowest_cost_node(costs)
    fastest_path.append(node)
    while node is not None:
        cost = costs[node]
        neighbors = graph[node]
        for n in neighbors.keys():
            new_cost = cost + neighbors[n]
            if costs[n] > new_cost:
                costs[n] = new_cost
                parents[n] = node
        processed.append(node)
        node = find_lowest_cost_node(costs)
        fastest_path.append(node)
        
    return fastest_path

运行结果:

在这里插入图片描述



四、总结

  • 广度优先搜索用于在非加权图中查找最短路径。
  • 狄克斯特拉算法用于在加权图中查找最短路径。
  • 仅当权重为正时狄克斯特拉算法才管用。
  • 如果图中包含负权边,请使用贝尔曼–福德算法。



版权声明:本文为North_City_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。