《图解算法》读书笔记(七) 狄克斯特拉算法

章节内容

  • 加权图
  • 狄克斯特拉算法
  • 图中的环,导致狄克斯特拉算法不管用

前言

在之前一个章节中,我们学会了使用广度优先搜索去查找最短路径。但其实在现实问题中,每个路径的长度都是不同的。
所以我们只是找到了一个最少连线的路径。在这一章节,我们将学会使用狄克斯特拉算法去查找加权图的最短路径。

使用狄克斯特拉算法

狄克斯特拉算法包含四个步骤:

  1. 找出加权最少的邻居
  2. 更新该节点的邻居的路径
  3. 重复第二个步骤,直到图中的每个节点都这样做了
  4. 计算最终路径

术语

权重:每条边上的关联数字
加权图:带权重的图
非加权图:不带权重的图
狄克斯特拉算法只适用于有向无环加权图

负权边

如果边的权有为负数的,也不适合用狄克斯特拉算法算法;
可使用另一种算法:贝尔曼-福德算法(Bellman-Ford)。
为什么不适合用狄克斯特拉算法?
狄克斯特拉算法是基于之前的节点的最短路径计算之后的节点,如果有负权边,则可能影响之前节点的最短路径,那样所有这个节点后的节点计算都存在问题。

实现

首先我们需要一个散列表存储图的数据,和上一章节不同,我们需要存储邻居的节点,以及到这些节点的开销,所以我们采用二维散列表。
比如:

graph = {}
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2

然后我们需要另一个散列表来存储从起点到每个节点的开销。对于还不知道开销的节点的值可以设置为无限大,python中可以表示为:
infinity = float("inf")
创建开销表的代码如下:

infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity

还需要一个存储父节点的散列表
创建这个散列表的代码如下:

parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None

最后需要一个散列表存储处理过的节点。
准备工作做好了,下面来看看算法步骤:

  1. 只要还有要处理的节点
  2. 获取离起点最近的节点
  3. 更新其邻居的开销
  4. 如果有邻居的开销被更新了,同时更新其父节点
  5. 将该节点标记为处理过
    重复以上五个步骤直至所有节点都已处理,代码如下:
node = find_lowest_cost_node(costs)
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)

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 not not in processed:
                  lowest_cost = cost
                  lowest_cost_node = node
      return lowest_cost_node

小结

  1. 广度优先搜索用于在非加权图中查找最短路径
  2. 狄克斯特拉算法用于在加权图中查找最短路径
  3. 仅当权重为正时狄克斯特拉算法管用
  4. 如果图中包含负权边,请使用Bellman-Ford算法
原文地址:https://www.cnblogs.com/prelude1214/p/13611342.html