标签:这一 col 集中 接下来 改变 自己人 roo size fat
并查集,首先它是一个集合,集合中的元素是互不相交的集合。如:{{1, 2, 3}, {4}, {5}}
并查集,集如其名。对并查集的有效操作只有两种:
一.合并两个子集
二.查询并查集的某两个元素是否属于同一个子集
传说,江湖中有一个神奇的小茗世家,江湖各处都有小茗世家的间谍,小茗世家无处不在,势力庞大,是江湖人最敬畏的对象。直到有一天,小茗甲与小茗乙产生了一些矛盾,但他们互不认识,他们调动自己手上小茗世家的力量,想要杀死对方,最后落得同归于尽的下场。他们两个都是小茗世家里位高权重的人物,这一战,直接损失了小茗世家四成的力量!!!小茗家主悲痛之余,决心要改变这种情况,不再使自相残杀的惨剧发生。他想出了一个好办法,既能使尽可能少的人知道小茗世家的间谍,又让他们不会自相残杀。每一个小茗间谍,都会有上级,上级又有上级,直到家主。如果,小茗丙和小茗丁想知道对方是不是自己人,只需要汇报自己的上级,一直汇报到家主。他们发现自己有同样的家主,就知道原来我们是自己人,就不会自相残杀了。
并查集就是这样的道理,每个子集都会有自己的代表元素,称为根元素。每一个元素又有自己的父亲元素(根元素的父亲元素是自己),这样想知道两个元素是否在同一子集,只需要知道他们的根元素是否相同。而合并两个子集,只需要一个根元素退位让贤,另一个根元素统领两个子集就行了。
对于并查集的两个有效操作,最重要的就是找到该元素的根元素。那么该怎么办呢?我们可以开一个father数组,存储每个元素的父亲元素。然后递归找到父亲的父亲……直到找到根元素为止。那么怎么判断一个元素找到了根元素呢?一个元素的父亲元素是自己,它就是根元素啦
1 #define maxsize 1000010 2 3 int father[maxsize]; 4 5 int getroot(int x) { 6 if (father[x] == x) { 7 return x; 8 } 9 else { 10 int father = father[x]; 11 return getroot(father); 12 } 13 }
但这样时间复杂度会不会很高呢?没关系,我们有更高级的路径压缩大法:
1 #define maxsize 1000010 2 3 int father[maxsize]; 4 5 int getroot(int x) { 6 if (father[x] == x) { 7 return x; 8 } 9 else { 10 int root = getroot(father[x]); 11 father[x] = root; 12 return root; 13 } 14 }
对于我们找过的元素,我们直接使他的父亲节点为根元素,这样就大大的减少了寻找根元素的时间了
接下来就是两个基本操作的实现了:
合并两个子集:
1 #define maxsize 1000010 2 3 int father[maxsize]; 4 5 void merge(int x, int y) { 6 int xroot = getroot(x); 7 int yroot = getroot(y); 8 9 father[yroot] = xroot; 10 }
查询并查集中某两个元素是否属于同一子集:
1 #define maxsize 1000010 2 3 int father[maxsize]; 4 5 bool isSameRoot(int x, int y) { 6 int xroot = getroot(x); 7 int yroot = getroot(y); 8 9 if (xroot == yroot) { 10 return true; 11 } 12 else { 13 return false; 14 } 15 }
标签:这一 col 集中 接下来 改变 自己人 roo size fat
原文地址:https://www.cnblogs.com/zymmyz/p/12557244.html