《算法图解》总结第 7 章:狄克斯特拉算法

仅用于记录学习,欢迎批评指正,大神勿喷

系列文章目录

《算法图解》总结第 1 章:二分查找、大O表示法;
《算法图解》总结第 2 章:数组和链表,选择排序;
《算法图解》总结第 3 章:while循环、递归、栈;
《算法图解》总结第 4 章:分而治之、快速排序;
《算法图解》总结第 5 章:散列表;
《算法图解》总结第 6 章:广度优先搜索;
《算法图解》总结第 7 章:狄克斯特拉算法;
《算法图解》总结第 8 章:贪婪算法
《算法图解》总结第 9 章:动态规划
《算法图解》总结第 10 章:K最近邻算法
《算法图解》总结第 11 章:十种算法简介


狄克斯特拉算法

广度优先搜索找出的是非加权图中段数最少的路径,狄克斯特拉算法找出的是加权图中最快(短)的路径。
狄克斯特拉算法包含四个步骤:
(1)找出“最便宜”的节点,即可在最短时间内到达的节点;
(2)更新该节点的邻居的开销;
(3)重复这个过程,直到对图中的每个节点都这样做了;
(4)计算最终路径。
狄克斯特拉算法只适用于有向无环图(DAG)。
如果有负权边,就不能使用狄克斯特拉算法,因为负权边会导致这种算法不管用,以下图为例:
假设要用乐谱换架子鼓,因为5>0,所以第一步找出的“最便宜”的点是海报,更新其邻居的开销。
在这里插入图片描述
第二步找出“最便宜”的未处理节点:黑胶唱片
在这里插入图片描述
从此图看出来海报节点也更新了开销,但是第一步海报节点已经处理过了,这是一个危险信号,因为节点一旦被处理,就意味着没有前往该节点的更便宜的途径,但是我们却找到了前往海报更便宜的途径。
架子鼓没有任何邻居,算法结束,因此最终开销如下:
在这里插入图片描述
由此可以得出乐谱换架子鼓的开销为35美元,但是从图中可以看出有一种交换方式为33美元,但是狄克斯特拉算法没有找到。为什么呢?理由:狄克斯特拉算法的假设是:对于处理过的海报节点,没有前往该节点的更短路径。这种假设仅在没有负权边时才成立。如果要找出在包含负权边的图中找出最短路径,可使用另一种算法:贝尔曼-福德算法。

应用案例

案例:运用狄克斯特拉算法找出下图从起点到终点的最快路径。
在这里插入图片描述

案例分析

要解决这个问题的代码,需要三个散列表:加权图、开销表、父节点表。

在这里插入图片描述

准备工作

一、表示加权图:

graph = {}
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2
graph["a"] = {}
graph["a"]["final"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["final"] = 5
graph["final"] = {}

二、 用散列表来存储每个节点的开销,即创建开销图

infinity = float("inf")  # 表示无穷大
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["final"] = infinity # 到终点的开销为无穷大

三、创建存储父节点的散列表

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

四、 创建数组,用于记录处理过的节点

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

实现狄克特斯拉算法

# 在未处理的节点中找出开销最小的节点
node = find_lowest_cost_node(costs)
# while循环在所有节点都被处理过后结束
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)
print("Cost from the start to each node:")
print(costs)

输出结果:

Cost from start to each node:
{'a': 5, 'b': 2, 'final': 6}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值