【朝夕的ACM笔记】目录与索引
最小生成树
一、基本概念
连通图:图中的任意两个节点都是相通的。
生成树:连通图的一个连通子图,其含有全部
最小生成树:边权和最小的生成树。
通俗地说,给定
下面介绍两种最小生成树的求法。
二、Kruskal算法
2.1 算法思想
Kruskal(克鲁斯卡尔)算法的基本思想如下:
①对所有给的边按其边权进行从小到大的排序。
②顺序遍历排序后的边。
③若当前边连接的两个节点已经在生成树上了,则跳过这条边。
④否则则将这条边加入生成树。
⑤当生成树上边数达到
由于需要判断两个节点是否已经连通,所以Kruskal算法还需要用到并查集。
Kruskal算法的时间复杂度为
2.2 参考代码
以下为【洛谷:P3366 最小生成树】的AC代码。
#include
三、Prim算法
3.1 算法思想
如果说Kruskal算法是基于边的算法,那么Prim算法则是基于点的算法。
Prim算法的基本思想如下:
①将任意一个点先加入生成树中。
②寻找这样的一个点,它尚未加入生成树,但与生成树内的点有边相连,且此边是所有满足条件的边中边权最小的。
③把这个点加入生成树,边权和加上这条边,然后重复②。
④直到所有点都加入生成树后,结束算法。
为了易于理解,我们举个例子来说明:
在上图中,我们重复一遍Prim的流程。
①将1加入生成树,此时生成树内节点为{1},边权和为0。
②与生成树内节点相连的未加入节点有2和3,最小权边为1-3,权为1。
③将3加入生成树,此时生成树内节点为{1,3},边权和为1。
④与生成树内节点相连的未加入节点有2,4,5,最小权边为1-2(或3-4),权为2。
⑤将2加入生成树,此时生成树内节点为{1,2,3},边权和为3。
⑥与生成树内节点相连的未加入节点有4,5,最小权边为3-4,权为2。
⑦将4加入生成树,此时生成树内节点为{1,2,3,4},边权和为5。
⑧与生成树内节点相连的未加入节点有5,最小权边为3-5,权为5。
⑨将5加入生成树,此时生成树已含全部节点,边权和为10,结束算法。
3.2 参考代码
要将以上步骤写成代码,则需要有一点点的改动。
我们每次加入点时,都将与该点连接的点其最小距离更新。
找点时就只需要遍历一遍所有未访问点就可以找到步骤②中的那个点了。
代码的时间复杂度是
以下为【洛谷:P3366 最小生成树】的AC代码。
#include
3.3 优先队列优化算法
我们会发现以下的代码本质是在找最小点:
for
所以我们可以使用优先队列来维护最小点。
注:我个人感觉Prim算法和最短路的Dijkstra算法是非常相像的,包括使用优先队列来优化这一点。
以下是优先队列维护Prim算法的参考代码,因为优先队列本质是由堆实现的,所以我称之为Prim_Heap。
Prim_Heap的时间复杂度为
#include
四、各算法的时间复杂度分析
注:引用博文https://blog.csdn.net/gykimo/article/details/8538275。
① Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。
②Prim_Heap在任何时候的时间复杂度都要优于朴素Prim和Kruskal(因为一般图的边数总是大于节点数的),但优先队列维护的代价是更大的内存空间使用。
③一般情况就使用Prim_Heap吧。