并查集
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询
问题。常常在使用中以森林来表示。集就是让每个元素构成一个单元素的集合,也就是按一定
顺序将属于同一组的元素所在的集合合并。。
在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的
集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个
集合中。这样的问题看起来似乎很简单,每次直接暴力查找即可,但是我们需要注意的问题
是,在数据量非常大的情况下,那么时间复杂度将达到O(N*n)(n为查询次数),那么这类问
题在实际应用中,如果采取上述方法去做的话,耗费的时间将是巨大的。而如果用常规的数据
结构去解决该类问题的话(顺序结构,普通树结构等),那么计算机在空间上也无法承受。所
以,并查集这种数据结构便应运而生了。
1 | 故事读完,并查集就会了~~~~~ |
下面我们来看并查集的实现。
int pre[1000]; 这个数组,记录了每个大侠的上级是谁。大侠们从1或者0开始编号(依据题意而定),pre[15]=3就表示15号大侠的上级是3号大侠。
如果一个人的上级就是他自己,那说明他就是掌门人了,查找到此为止。也有孤家寡人自成一派的,比如欧阳锋,那么他的上级就是他自己。
每个人都只认自己的上级。比如胡青牛同学只知道自己的上级是杨左使。张无忌是谁?不认识!要想知道自己的掌门是谁,只能一级级查上去。
1 | //find函数来查找队长 |
路径压缩(有时候在find函数里面可以省略)
1 | 再来看看路径压缩算法。建立门派的过程是用join函数两个人两个人地连接起来的,谁当谁的手下完全随机。最后的树状结构会变成什么样,我也无法预知,一字长蛇阵也有可能。这样查找的效率就会比较低下。最理想的情况就是所有人的直接上级都是掌门,一共就两级结构,只要找一次就找到掌门了。哪怕不能完全做到,也最好尽量接近。这样就产生了路径压缩算法。 |
再来看看join函数,就是在两个点之间连一条线,这样一来,原先它们所在的两个板块的所有点就都可以互通了。
这在图上很好办,画条线就行了。但我们现在是用并查集来描述武林中的状况的,一共只有一个pre[]数组,该如何实现呢?
还是举江湖的例子,假设现在武林中的形势如图所示。虚竹帅锅与周芷若MM是我非常喜欢的两个人物,他们的终极boss分别是玄慈方丈和灭绝师太,
那明显就是两个阵营了。我不希望他们互相打架,就对他俩说:“你们两位拉拉勾,做好朋友吧。”他们看在我的面子上,同意了。
这一同意可非同小可,整个少林和峨眉派的人就不能打架了。这么重大的变化,可如何实现呀,要改动多少地方?其实非常简单,
我对玄慈方丈说:“大师,麻烦你把你的上级改为灭绝师太吧。这样一来,两派原先的所有人员的终极boss都是师太,那还打个球啊!
反正我们关心的只是连通性,门派内部的结构不要紧的。”玄慈一听肯定火大了:“我靠,凭什么是我变成她手下呀,怎么不反过来?
我抗议!”于是,两人相约一战,杀的是天昏地暗,风云为之变色啊,但是啊,这场战争终究会有胜负,胜者为王。弱者就被吞并了。
反正谁加入谁效果是一样的,门派就由两个变成一个了。这段函数的意思明白了吧?
1 | //join函数连接 注意:join函数一般写在find函数下边 |
最小生成树
1.最小生成树概念:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树(使所有点联通+建立所有边的代价和最小)
2.最小生成树应用:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,
且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。
1.Prim(普里姆)算法(加点法)
(1)算法思想:以任意一点为树根出发,集合V是已经确定最短路的点集合,集合U是没有确立最短路的集合。初始时只有树根点在V中。
每一次循环就代表要修建一条最短路,到达没到达的点(U),我们只能从已经建成的局部最短路点集V中选取V中所有已确定点能到达的所有其他点里面最小的来建设,
有点贪心思想,每次选取代价最小的路,逐渐完善点,知道恰好覆盖所有的点。
1 |
|
2.克鲁斯克尔(Kruskal)算法(加边法)(贪心+并查集=最小生成树)
算法思想:最小生成树最后一定是只有n-1条边!所以我们只要选取最小的n-1条边来吧n个点联通起来即可,但是注意不能产生回路,于是我们就用到了并查集!
记Graph中有v个顶点,e条边;
新建图Graphnew,Graphnew中拥有原图中的v个顶点,但没有边;
将原图Graph中所有e条边按权值从小到大排序;
循环:从权值最小的边开始,判断并添加每条边,直至添加了n-1条边:
注意:加边的条件是不产生回路!即要连接的两定点不在一个集合里面!(并查集判断是否可以加边)
Kruskal算法的证明。假设图连通,我们证明Krusal算法得到一棵最小生成树。我们假设Kruskal算法得到的树是K (注意我们已经假设Kruskal算法一定可以得到生成树)。假设T是一棵最小生成树,并且K ≠T, K中显然至少有一条边。我们找到在K中,而不在T中最小权值的边e。
把e加入T中,则形成一个圈,删掉这个圈中不在K中的边f,得到新的生成树T’。
f的存在性,如果全里面所有的边都在K中,则K包含圈,矛盾。
1 |
|