并查集详解

Posted downrainsun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集详解相关的知识,希望对你有一定的参考价值。

       使用并查集查找时,如果查找次数很多,那么使用朴素版的查找方式肯定要超时。比如,有一百万个元素,每次都从第一百万个开始找,这样一次运算就是10^6,如果程序要求查找个一千万次,这样下来就是10^13,肯定要出问题的。

  这是朴素查找的代码,适合数据量不大的

int findx(int x)
{
int r=x;
while(parent[r] !=r)
r=parent[r];
return r;
}
 
 

    下面是采用路径压缩的方法查找元素:

int find(int x) //查找x元素所在的集合,回溯时压缩路径
{
if (x != parent[x])
{
parent[x] = find(parent[x]); //回溯时的压缩路径
} //从x结点搜索到祖先结点所经过的结点都指向该祖先结点
return parent[x];
}
 
 

    上面是一采用递归的方式压缩路径, 但是,递归压缩路径可能会造成溢出栈,我曾经因为这个RE了n次,下面我们说一下非递归方式进行的路径压缩:

接下来是合并的代码,保证能够把小的子树接到大的子树上面

有很多组学生,在同一个组的学生经常会接触,也会有新的同学的加入。但是SARS是很容易传染的,只要在改组有一位同学感染SARS,那么该组的所有同学都被认为得了SARS。现在的任务是计算出有多少位学生感染SARS了。假定编号为0的同学是得了SARS的。

解题思路---->显然并查集了。并查集的详细解释在可以点击 并查集(不相交集合)进行学习。采用num[]存储该集合中元素个数,并在集合合并时更新num[]即可。然后找出0所在的集合的根节点x,因此,num[x]就是answer了。

  1. Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->#include <stdio.h>//by ktyanny  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. const int MAXN = 30001; /*结点数目上线*/  
  6. int pa[MAXN];    /*p[x]表示x的父节点*/  
  7. int rank[MAXN];    /*rank[x]是x的高度的一个上界*/  
  8. int num[MAXN];/*num[]存储该集合中元素个数,并在集合合并时更新num[]即可*/  
  9.   
  10. void make_set(int x)  
  11. {/*创建一个单元集*/  
  12.     pa[x] = x;  
  13.     rank[x] = 0;  
  14.     num[x] = 1;  
  15. }  
  16.   
  17. int find_set(int x)  
  18. {/*带路径压缩的查找*/  
  19.     /*保存待查找的数*/  
  20.     int r = x, temp;  
  21.     /*找到根节点*/  
  22.     while(pa[r] != r) r = pa[r];  
  23.     while(x != r)  
  24.     {  
  25.         temp = pa[x];  
  26.         pa[x] = r;  
  27.         x = temp;  
  28.     }  
  29.     return x;  
  30.     //if(x != pa[x]) //注释掉的其实也是可以的,不过不想用递归来做啦  
  31.     //    pa[x] = find_set(pa[x]);  
  32.     //return pa[x];  
  33. }  
  34.   
  35. /*按秩合并x,y所在的集合*/  
  36. void union_set(int x, int y)  
  37. {  
  38.     x = find_set(x);  
  39.     y = find_set(y);  
  40.     if(x == y)return ;  
  41.     if(rank[x] > rank[y])/*让rank比较高的作为父结点*/  
  42.     {  
  43.         pa[y] = x;  
  44.         num[x] += num[y];  
  45.     }  
  46.     else   
  47.     {  
  48.         pa[x] = y;  
  49.         if(rank[x] == rank[y])  
  50.             rank[y]++;  
  51.         num[y] += num[x];  
  52.     }  
  53. }  
  54.   
  55. //answer to 1611   
  56. int main()  
  57. {  
  58.     int n, m, x, y, i, t, j;  
  59.     while(scanf("%d%d", &n, &m))  
  60.     {  
  61.         if(m==n && n == 0) break;  
  62.         if(m == 0)  
  63.         {  
  64.             cout << "1 "; continue;  
  65.         }  
  66.         for(i = 0; i < n; i++)  
  67.             make_set(i);  
  68.         for(i = 0; i < m; i++)  
  69.         {  
  70.             scanf("%d", &t);  
  71.             scanf("%d", &x);  
  72.             for(j = 1; j < t; j++){  
  73.                 scanf("%d", &y);  
  74.                 union_set(x, y);  
  75.                 x = y;  
  76.             }  
  77.         }  
  78.         x = find_set(0);/*找到0所在的树的树根*/  
  79.         //int ans = 0;  
  80.         //for(i = 0; i < n; i++)  
  81.         //    if(pa[i] == x)  
  82.         //        ans++;  
  83.         //cout << ans << endl;  
  84.         cout << num[x] << endl;  
  85.     }  
  86.     return 0;  
  87. }  

以上是关于并查集详解的主要内容,如果未能解决你的问题,请参考以下文章

详解并查集

详解并查集

详解并查集

并查集算法详解

并查集详解

详解并查集