并查集详解

使用并查集查找时,如果查找次数很多,那么使用朴素版的查找方式肯定要超时。比如,有一百万个元素,每次都从第一百万个开始找,这样一次运算就是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. const int MAXN = 30001; /*结点数目上线*/
  5. int pa[MAXN];    /*p[x]表示x的父节点*/
  6. int rank[MAXN];    /*rank[x]是x的高度的一个上界*/
  7. int num[MAXN];/*num[]存储该集合中元素个数,并在集合合并时更新num[]即可*/
  8. void make_set(int x)
  9. {/*创建一个单元集*/
  10. pa[x] = x;
  11. rank[x] = 0;
  12. num[x] = 1;
  13. }
  14. int find_set(int x)
  15. {/*带路径压缩的查找*/
  16. /*保存待查找的数*/
  17. int r = x, temp;
  18. /*找到根节点*/
  19. while(pa[r] != r) r = pa[r];
  20. while(x != r)
  21. {
  22. temp = pa[x];
  23. pa[x] = r;
  24. x = temp;
  25. }
  26. return x;
  27. //if(x != pa[x]) //注释掉的其实也是可以的,不过不想用递归来做啦
  28. //    pa[x] = find_set(pa[x]);
  29. //return pa[x];
  30. }
  31. /*按秩合并x,y所在的集合*/
  32. void union_set(int x, int y)
  33. {
  34. x = find_set(x);
  35. y = find_set(y);
  36. if(x == y)return ;
  37. if(rank[x] > rank[y])/*让rank比较高的作为父结点*/
  38. {
  39. pa[y] = x;
  40. num[x] += num[y];
  41. }
  42. else
  43. {
  44. pa[x] = y;
  45. if(rank[x] == rank[y])
  46. rank[y]++;
  47. num[y] += num[x];
  48. }
  49. }
  50. //answer to 1611
  51. int main()
  52. {
  53. int n, m, x, y, i, t, j;
  54. while(scanf("%d%d", &n, &m))
  55. {
  56. if(m==n && n == 0) break;
  57. if(m == 0)
  58. {
  59. cout << "1\n"; continue;
  60. }
  61. for(i = 0; i < n; i++)
  62. make_set(i);
  63. for(i = 0; i < m; i++)
  64. {
  65. scanf("%d", &t);
  66. scanf("%d", &x);
  67. for(j = 1; j < t; j++){
  68. scanf("%d", &y);
  69. union_set(x, y);
  70. x = y;
  71. }
  72. }
  73. x = find_set(0);/*找到0所在的树的树根*/
  74. //int ans = 0;
  75. //for(i = 0; i < n; i++)
  76. //    if(pa[i] == x)
  77. //        ans++;
  78. //cout << ans << endl;
  79. cout << num[x] << endl;
  80. }
  81. return 0;
  82. }

原文地址:https://www.cnblogs.com/downrainsun/p/9733647.html

时间: 2024-07-29 16:46:25

并查集详解的相关文章

并查集详解(转)

并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了.以前我无法解决的一类问题竟然可以用如此简单高效的方法搞定.不分享出来真是对不起party了.(party:我靠,关我嘛事啊?我跟你很熟么?) 来看一个实例,杭电1232畅通工程 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块.像畅通工程这题,问还需要修

并查集详解(转自一个很有才的大神)膜拜

并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了.以前我无法解决的一类问题竟然可以用如此简单高效的方法搞定.不分享出来真是对不起party了.(party:我靠,关我嘛事啊?我跟你很熟么?) 来看一个实例,杭电1232畅通工程 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块.像畅通工程这题,问还需要修

并查集详解 (转)

http://blog.csdn.net/dellaserss/article/details/7724401 我从CSDN转的文章,原文作者我也不懂是谁,文章写得真的是诙谐幽默,使得内容更容易理解了. 来看一个实例,杭电OJ 1232畅通工程 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可).问最少还需要建

【转】并查集详解

来看一个实例,杭电1232畅通工程 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块.像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支.如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了:如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了:如

读书笔记 之 数据结构(并查集详解)(POJ1703)

<ACM/ICPC算法训练教程>读书笔记-这一次补上并查集的部分.将对并查集的思想进行详细阐述,并附上本人AC掉POJ1703的Code. 在一些有N个元素的集合应用问题中,通常会将每个元素构成单元素集合,然后按照一定顺序将同属一组的集合合并,期间要反复查找每一个元素在哪个集合中.这类问题往往看似简单,但是数据量很大,因此容易造成TLE或MLE,也就是空间度和时间度极其复杂.因此在这里,我们引入一种抽象的特殊数据结构——并查集. 并查集:类似一个族谱,每个结点均有一个father[x]来表示x

并查集详解---(转)

并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了.以前我无法解决的一类问题竟然可以用如此简单高效的方法搞定.不分享出来真是对不起party了.(party:我靠,关我嘛事啊?我跟你很熟么?) 来看一个实例,杭电1232畅通工程 首 先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个 点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块.像畅通工程这题,问还需

[转]并查集详解

来看一个实例,杭电1232畅通工程 首 先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个 点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块.像畅通工程这题,问还需要修几条路,实质就是求有几个连 通分支.如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了:如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起 来,那么所有的点都是连起来

并查集详解及模板

概念: p { margin-bottom: 0.25cm; direction: ltr; color: #000000; line-height: 120%; text-align: justify; orphans: 0; widows: 0 } p.western { font-family: "Calibri", "Lucida Sans Unicode", sans-serif; font-size: 10pt } p.cjk { font-family

mysql---字符集详解

常用的字符集包括ASCII ,GB2312 , GBK , UTF-8 ,Unicode 首先要知道 ASCII编码: 用一个字节来标识0-9的数字.大小写字母.及一些标点和不可见字符.1个字节8位,可以有256种组合.标准的ASCII编码只利用一个字节的后7位(128种组合),最高位用作奇偶校验. 范围为0000 0000 - 0111 1111 即 0-127 因为ASCII最多只有256种组合,中国汉字成千上万,所以需要更多的字节来表示一个汉字,常见中文编码的有GB2312和GBK. GB