并查集的理解
Posted Hunter丶安
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集的理解相关的知识,希望对你有一定的参考价值。
从数学的角度来说,假设我们有n个集合{{..},{..},.......},而每个集合内又有众多的元素{A,B,C.....},{1,2,3,4......}等等,每个集合的元素都存在这或多或少的联系,什么联系不清楚,但我们只需知道存在这样的关系即可;而突然,我们希望让集合之间也联系起来,而又不想一个一个的将它们绑到一起,那么,并查集就是解决它们之间联系的数据结构。树就是我们需要用到的一种连接结构。
并查集一般包含三个主体:
1. makeSet();建立一个数组并储存每一个数的节点(可以看成他们所处在的队伍的编号);
for(int i=1;i<=n;i++) per[i] = i;
2.Find(int x);查找x的根节点,也就是x所队伍的代表人(代表人特征 :Find(n)=n);
int find(int p) { while(p != id[p]) { p = id[p]; } return p; }
-
-
-
- 在这里我们简单的思考一下Find的效率问题,我们通过不断个根节点添加子节点的方式(也就是树),将两者联系在一起,如果树的路径变得很长,就需要从下到上一直访问下去,效率就大大降低,因此我们需要想个办法决绝路径过长的问题;
- 路径压缩:所有的子节点,还是子子节点,全部直接连接到根节点上去;
int Find(int x) { if(per[x]!=x) per[x] = Find(per[x]);//把属于一个队伍的人全部和老大联系起来; return per[x]; }
-
-
3.unionSet(int x,int y) :将两个关联的点连接起来;
void Union(int a,int b) { //if(Find(a)!=Find(b)) //per[Find(a)]=b;
int ax = Find(a);
int bx = Find(b);
if(ax!=bx)
per[ax]=b;
}
在这里,我们考虑下per[ax] = b,为甚么一定是这样的,而不是per[bx] = a呢?是不是很奇怪;其实无论哪种都没有错,都可以将两者联系;但我们思考一下,如果总是固定的一个添加方法,如果出现子树远远大于根数,那么连接起来的情况就会很离奇。树的路径就会变得很大,因此我们自然会想到,那就把小的连接到大的不就好了;而Find()方法的效率取决于树的路径大小,那么我们不就又提高了代码的效率了吗?
void unionSet(int p, int q) { int i = find(p); int j = find(q); if (i == j) return; if (sz[i] < sz[j])//sz[]用来记录树的size; { id[i] = j; sz[j] += sz[i]; } else { id[j] = i; sz[i] += sz[j]; } count--; }
以上是关于并查集的理解的主要内容,如果未能解决你的问题,请参考以下文章