图论之最小生成树(C++) – 找最近的朋友拉拉手
最小生成树(MST, Minimum Spanning Tree)顾名思义就是一棵树,这颗树要满足两个要求,一是这是一棵树,既然是一颗树就不能有环,也就是如果在找到的最小生成树中去掉任意一条边,都会将这棵树分成两颗,数学说法是两个连通分量,二是所有图中的顶点都在树中;想象一个房间里塞满了人,全是抠脚大汉,油腻多汗,出去的条件是所有人都手拉手(一个人可以和多个人拉手),但是只有相互认识的大汉才能拉手,如果你是房主,当然你也在房间里,而且在抠脚大汉全都出去前你要坚守阵地(没办法,房主嘛,必须要有但当),为了能尽快脱离浓郁的氛围,最好的做法就是让相互认识又离得最近的大汉拉起手,这里将大汉看作顶点,将相互认识看作边,最小生成树的各边就是各个抠脚大叔能拉起手的最短路径;
最小生成树的基本玩法有两个,一个是Kruskal算法,另一个是Prim算法;
- Kruskal算法:整体思路就是最小生成树中的边一定是尽可能小的边,把所有不重复顶点的最短边放到一起就是最小生成树,这其中用了并查集(需对并查集有一定了解,可以查看并查集相关),其局限性是只能用于无向图;
- Prim算法:Prim生成最小生成树的方式用的是切分的玩法,也就是从图中随便切出一块,这时被切掉的边中最小的边必然在在最小生成树中,不断地将最小边连接的顶点加入到最小生成树的顶点集合中,直到所有顶点都加入,最小生成树就生成了;Prim玩法需要用索引堆进行优化,这里为了方便理解,没有用索引堆(对索引堆优化有兴趣可以先看下索引堆的实现),而是用最小堆(最大堆传送门,最小堆同理实现,只是改下大于小于号)的方式实现,这时也就被称为Lazy Prim;
- 带权图的边类:边可能有权值,设置一个边类,即可设定是否有向,也可以存储权值;
- 边的遍历是在图中有用到自定义的图的边的遍历器,其实可以在图中加一个方法返回边来简化,通过设置一个遍历器,优化了空间复杂度,相关图和遍历器的设置在图遍历传送门;