前几天有些事情耽搁了,不过好在图论我完全不会,所以偷看代码思路也没有任何的心理负担,做起来反而相对独立思考更快一点点。
47. 参加科学大会(dijkstra(堆优化版)精讲 )
本来一开始以为这个堆优化版相对于朴素版就类似于kruskal算法之于prim算法,定睛一看才发现并非如此,区别还是有很多的。
因为是时隔好几天回来写代码,有一种恍若隔世的感觉,甚至有点忘了kruskal/prim以及dijkstra算法都是针对什么问题的,所以就问了一下GPT,没想到还是挺有收获的:
1. 计算目标的不同:
Prim 和 Kruskal:
- 目标:构造一棵 最小生成树 (Minimum Spanning Tree, MST)。
- 目的在于找到一个无向图中连接所有节点的总权重最小的树。
- 生成树不关注特定的起点或终点,而是全图的一种全局性质。
- MST 确保所有节点都被连接且总权重最小,但并不保证某个节点到另一个节点的最短路径。
Dijkstra:
- 目标:计算一个特定起点到图中所有其他节点的 最短路径。
- 主要用于单源最短路径问题。
- 仅关注从起点到终点的最短权重和路径,而不会覆盖所有节点。
2. 输入图类型的不同:
特性 | Prim/Kruskal | Dijkstra |
---|---|---|
图类型 | 无向图 | 有向图或无向图均可 |
边权值特性 | 权值非负(负权值不影响) | 权值必须非负 |
3. 使用的算法思想:
Prim:
- 基于 贪心法 构造 MST。
- 从某个节点出发,每次选择权值最小的边扩展树,直到包含所有节点。
- 支持动态增加节点,并使用优先队列进行优化(时间复杂度可以为 O((n+m)logn)O((n + m) \log n)O((n+m)logn))。
Kruskal:
- 基于 贪心法 和 并查集,构造 MST。
- 将图中的所有边按权值排序,然后从最小权值开始加入树中,同时避免形成环。
- 更适合处理稀疏图,时间复杂度为 O(mlogm)O(m \log m)O(mlogm),其中 mmm 是边的数量。
Dijkstra:
- 基于 贪心法 和优先队列。
- 从起点出发,每次选择当前路径最短的节点,更新邻接节点的最短距离,直到所有节点的最短距离确定。
- 时间复杂度为 O((n+m)logn)O((n + m) \log n)O((n+m)logn)。
4. 结果的不同:
Prim 和 Kruskal:
- 输出是一个包含 n−1n-1n−1 条边的 最小生成树。
- 生成树覆盖了所有节点,但并不提供任何节点对之间的最短路径信息。
- 最小生成树 ≠ 最短路径树(MST 不适合直接用于单源最短路径问题)。
Dijkstra:
- 输出是从起点到其他节点的最短路径。
- 生成的树是以起点为根的 最短路径树。
- 最短路径树并不一定是最小生成树,因为两者优化目标不同。
简单来说,kruskal/prim的最终目的是要找一个最小生成树把所有节点都连起来,而dijkstra单纯只是想知道从a到b的最短路径是什么。
那次是很容易想到一个新的问题:最小生成树当中两个节点之间的距离是最短路径吗?
并不是。比如最小生成树当中有可能是a-d,c-d,那我从a到c就是a-d加上c-d的权值,那假设我有一个直接连接a-c的边且权值比a-d加上c-d的权值要小,那最小生成树里的这个权值就不是