并查集

耗尽温柔 提交于 2020-02-26 19:29:28

并查集:(union-find sets) 是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多,如其求无向图的连通分量个数等。最完美的应用当属:实现Kruskal算法求最小生成树。

并查集的精髓:

1、make_set(x) 把每一个元素初始化为一个集合

初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变)。

2、find_set(x) 查找一个元素所在的集合(有两种方法:朴素查找和采用路径压缩的方法查找,其中路径压缩有递归和非递归)

查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。

3、Union(x,y) 合并x,y所在的两个集合:
利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。

并查集的优化

1、find_set(x)时 路径压缩
寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次find_set(x)都是O(n)的复杂度,因此需要路径压缩,即当我们经过"递推"找到祖先节点后,"回溯"的时候顺便将它的子孙节点都直接指向祖先,这样以后再次find_set(x)时复杂度就变成O(1)了,路径压缩方便了以后的查找。

2、Union(x,y)时按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。

View Code
const int size = 100;int father[size];int rank[size];//把每个元素初始化为一个集合 void makes_set(){    for(int i = 1; i <= n; i ++){        father[i] = i;            rank[i] = 1;    }        return ;} //查找一个元素所在的集合,找到这个元素所在集合的祖先,这//是并查集判断和合并的最终依据。判断两个元素是否属于同一个集合,只要看它们所在集合的祖先是否相同 int find_set(int x){    if(x != father[x]){        father[x] = find_set(father[x]);        }        return father[x];}//非递归路径压缩 int find_set(int x){    int r = x;    while(r != father[r]){ //查找根节点         r = father[r];        //找到根节点,用r记录     }     int k = x;    while(k != r){  //非递归路径压缩         j = father[k];  //用j暂存k的父节点         father[k] = r;  //k指向根节点         k = j;  //k移到父节点     }    return r;  //返回根节点的值   }//利用find_set找到两个集合的祖先,将一个集合的祖先指向另一个集合的祖先 void Union(int x, int y){    x = find_set(x);    y = find_set(y);    if(x == y){        return ;        }        if(rank[x] < rank[y]){        father[x] = y;        }    else{        if(rank[x] == rank[y]){            rank[x] ++;        }        else{            father[y] = x;        }    return ;}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!