《算法图解》学习笔记—第7章 狄克斯特拉算法

前言

  • 加权图(weighted graph)——带权重的图
  • 狄克斯特拉算法,让你能够找出加权图中前往X的最短路径。
  • 狄克斯特拉算法只适用于有向无环图(directed acyclic graph,DAG)。
  • 狄克斯特拉算法假设:对于处理过的海报节点,没有前往该节点的更短路径。这种假设仅在没有负权边时才成立。因此,不能将狄克斯特拉算法用于包含负权边的图。在包含负权边的图中,要找出最短路径,可使用另一种算法——贝尔曼-福德算法(Bellman-Ford
    algorithm)。

狄克斯特拉算法

算法步骤

在这里插入图片描述
要计算非加权图中的最短路径,可使用广度优先搜索。要计算加权图中的最短路径,可使用狄克斯特拉算法

狄克斯特拉算法包含4个步骤。
(1) 找出“最便宜”的节点,即可在最短时间内到达的节点。
(2) 更新该节点的邻居的开销,其含义将稍后介绍。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。

实例:拿一本乐谱换架钢琴。
在这里插入图片描述
第一步:找出最便宜的节点。在这里,换海报最便宜,不需要支付额外的费用。找出图中最便宜的节点,并确保没有到该节点的更便宜的路径!

第二步:计算前往该节点的各个邻居的开销。
在这里插入图片描述
现在的表中包含低音吉他和架子鼓的开销。这些开销是用海报交换它们时需要支付的额外费用,因此父节点为海报。这意味着,要到达低音吉他,需要沿从海报出发的边前行,对架子鼓来说亦如此。

再次执行第一步:下一个最便宜的节点是黑胶唱片——需要额外支付5美元。

再次执行第二步:更新黑胶唱片的各个邻居的开销。
在这里插入图片描述
你更新了架子鼓和吉他的开销!这意味着经“黑胶唱片”前往“架子鼓”和“吉他”的开销更低,因此你将这些乐器的父节点改为黑胶唱片。
下一个最便宜的是吉他,因此更新其邻居的开销。
在这里插入图片描述
最后,对最后一个节点——架子鼓,做同样的处理。
在这里插入图片描述
通过沿父节点回溯,便得到了完整的交换路径。
在这里插入图片描述

实现

使用代码来实现狄克斯特拉算法,这里以下面的图为例。
在这里插入图片描述

要编写解决这个问题的代码,需要三个散列表。
在这里插入图片描述
存储邻居的散列表代码如下:

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

在这里插入图片描述
因此graph[“start”]是一个散列表。要获取起点的所有邻居,可像下面这样做。

>>> print graph["start"].keys()
["a", "b"]

有一条从起点到A的边,还有一条从起点到B的边。要获悉这些边的权重,可以

>>> print graph["start"]["a"]
2
>>> print graph["start"]["b"]
6

添加其他节点及其邻居。

graph["a"] = {}
graph["a"]["fin"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["fin"] = 5
graph["fin"] = {}

创建开销表的代码如下:

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

存储父节点的散列表的代码如下:

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

狄克斯特拉算法代码如下:

node = find_lowest_cost_node(costs) #在未处理的节点中找出开销最小的节点
while node is not None: #这个while循环在所有节点都被处理过后结束
     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 node not in processed:#如果当前节点的开销更低且未处理过
            lowest_cost = cost # 就将其视为开销最低的节点
            lowest_cost_node = node
     return lowest_cost_node

代码的执行过程:

找出开销最低的节点。
在这里插入图片描述
获取该节点的开销和邻居。
在这里插入图片描述
遍历邻居。
在这里插入图片描述
每个节点都有开销。开销指的是从起点前往该节点需要多长时间。在这里,你计算从起点出发,经节点B前往节点A(而不是直接前往节点A)需要多长时间。
在这里插入图片描述
接下来对新旧开销进行比较。
在这里插入图片描述
找到了一条前往节点A的更短路径!因此更新节点A的开销。
在这里插入图片描述
这条新路径经由节点B,因此节点A的父节点改为节点B。
在这里插入图片描述
现在回到了for循环开头。下一个邻居是终点节点。
在这里插入图片描述
经节点B前往终点需要多长时间呢?
在这里插入图片描述
需要7分钟。终点原来的开销为无穷大,比7分钟长。设置终点节点的开销和父节点。
在这里插入图片描述
你更新了节点B的所有邻居的开销。现在,将节点B标记为处理过。
在这里插入图片描述
找出接下来要处理的节点,直到处理完所有的节点。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
狄克斯特拉算法Dijkstra's algorithm)是一种用于解决单源最短路径问题的经典算法。它可以找到从一个顶点到其他所有顶点的最短路径。 以下是狄克斯特拉算法的Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int INF = Integer.MAX_VALUE; public static void dijkstra(int[][] graph, int start) { int n = graph.length; int[] dist = new int[n]; boolean[] visited = new boolean[n]; Arrays.fill(dist, INF); dist[start] = 0; for (int i = 0; i < n - 1; i++) { int minDist = INF; int minIndex = -1; for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; minIndex = j; } } visited[minIndex] = true; for (int j = 0; j < n; j++) { if (!visited[j] && graph[minIndex][j] != 0 && dist[minIndex] != INF && dist[minIndex] + graph[minIndex][j] < dist[j]) { dist[j] = dist[minIndex] + graph[minIndex][j]; } } } // 打印最短路径 System.out.println("顶点\t最短距离"); for (int i = 0; i < n; i++) { System.out.println(i + "\t" + dist[i]); } } public static void main(String[] args) { int[][] graph = { {0, 4, 0, 0, 0, 0, 0, 8, 0}, {4, 0, 8, 0, 0, 0, 0, 11, 0}, {0, 8, 0, 7, 0, 4, 0, 0, 2}, {0, 0, 7, 0, 9, 14, 0, 0, 0}, {0, 0, 0, 9, 0, 10, 0, 0, 0}, {0, 0, 4, 14, 10, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 1, 6}, {8, 11, 0, 0, 0, 0, 1, 0, 7}, {0, 0, 2, 0, 0, 0, 6, 7, 0} }; dijkstra(graph, 0); } } ``` 这段代码实现了狄克斯特拉算法,通过传入一个邻接矩阵表示的图和起始顶点,计算出从起始顶点到其他所有顶点的最短路径,并打印出最短距离。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值