并查集

  并查集(Union-Find Sets)是一种非常精巧而实用的集合,集合中的每个元素仍是一个集合,即它是集合的集合。在并查集中的元素(集合)内部进行查找操作以及并查集中的元素(集合)之间的合并操作的时间复杂度均可视为O(1),它主要用于处理一些不相交集合的合并问题,合并之前,需要先判断两个元素是否属于同一集合,这就需要用查找操作来实现,即先查找后合并

  并查集的原理也比较简单,逻辑上使用树来表示集合,树的每个节点就表示集合中的一个元素,指针指向其直接父节点,根结点对应的元素就是该集合的“代表”,指针指向自己,沿着每个节点的指针不断向上查找,最终就可以找到该树的根节点,即该集合的代表元素。如下图所示。

                    

  上图有两个集合,其中第一个集合为 {a,b,c,d},代表元素是 a;第二个集合为 {e,f,g},代表元素是 e,它们整体是一个并查集 { {a,b,c,d},  {e,f,g} }。

  假设使用一个足够长的数组来存储集合元素,并查集在初始化时构造出下图的森林,其中每个元素都是一个单元素集合,即父节点是其自身:

             

  并查集内的合并操作非常简单,就是将一个集合的树根指向另一个集合的树根。这里可以应用一个简单的启发式策略——按秩合并。该方法使用秩来表示树高度的上界,在合并时,总是将具有较小秩的树根指向具有较大秩的树根。简单的说,就是总是将较矮的树作为子树,添加到较高的树中。如下图所示,第一个集合为 {a,b,c,d},代表元素是 a,秩为3;第二个集合为 {e,f},代表元素是 e,秩为2;合并两个集合得到集合 {a,b,c,d,e,f},代表元素是 a,秩为3。

         

  对于并查集内的查找操作,其目的就是找到所在集合的代表元素,如果每次都沿着父节点向上查找,那时间复杂度就是树的高度,完全不可能达到常数级。这里需要应用另一种简单有效的启发式策略——路径压缩。在每次查找时,令查找路径上的每个节点都直接指向我们要找的根节点,如下图所示。

         

  

  

  关于并查集,一些常见的用途有求连通子图(判断连通性)、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。

  习题:畅通工程  

  题目来源:HDOJ-1232-畅通工程

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

  源码如下:

  1. #include<iostream>
  2. using namespace std;
  3.  
  4. const int CMAX = ; // 最多1000个城镇(编号从1开始)
  5.  
  6. int parent[CMAX]; // 存放各结点的直接父节点,对于根结点,parent指向本身
  7. int Rank[CMAX]; // 存放各结点的秩,即该结点在子树中的高度(实际上只有根结点的秩在按秩合并时才有价值)
  8.  
  9. int Find(int x) // 带有路径压缩的查找过程,返回根结点(代表元素)
  10. {
  11. if (x != parent[x])
  12. parent[x] = Find(parent[x]);// 沿着查找路径递归向上直到找到根
  13. return parent[x]; // 找到根,开始回溯,进行路径压缩
  14. }
  15.  
  16. void Union(int x, int y) // 按秩合并两个树(集合),让具有较小秩的根指向具有较大秩的根
  17. {
  18. int root1 = Find(x);
  19. int root2 = Find(y);
  20. if (root1 == root2) // 根结点相同,无需合并
  21. return;
  22. if (Rank[root1] < Rank[root2])
  23. {
  24. parent[root1] = root2;
  25. }
  26. else
  27. {
  28. parent[root2] = root1;
  29. if (Rank[root1] == Rank[root2]) // 只有秩相等时需要递增根的秩(大树合并小树,根秩不变)
  30. Rank[root1]++;
  31. }
  32. }
  33.  
  34. int main()
  35. {
  36. int N, M; // 城镇数目和道路数目
  37. while (scanf("%d%d", &N, &M) && N) // 当N为0时,输入结束
  38. {
  39. int tree_num = ; // 树的个数,即连通分支数
  40. for (int i = ; i <= N; i++) // 初始化每个结点独自成树,秩为1
  41. {
  42. parent[i] = i;
  43. Rank[i] = ;
  44. }
  45. int city1, city2;
  46. for (int i = ; i <= M; i++) // 读取M条道路,合并连通的城镇
  47. {
  48. scanf("%d%d", &city1, &city2);
  49. Union(city1, city2);
  50. }
  51. for (int i = ; i <= N; i++)
  52. {
  53. if (parent[i] == i) // 每找到一个根就有一个连通分支
  54. tree_num++;
  55. }
  56. printf("%d\n", tree_num - ); // 把这些连通分支连起来需要修tree_num-1条路
  57. }
  58. return ;
  59. }

  提交结果:

  参考资料:   《算法导论第3版》—— 21.3 不相交集合森林

          http://blog.csdn.net/dellaserss/article/details/7724401

          http://www.cnblogs.com/cyjb/p/UnionFindSets.html

并查集——HDOJ-1232-畅通工程的更多相关文章

  1. 并查集 HDOJ 1232 畅通工程

    题目传送门 /* 并查集(Union-Find)裸题 并查集三个函数:初始化Init,寻找根节点Find,连通Union 考察:连通边数问题 */ #include <cstdio> #i ...

  2. 并查集专题: HDU1232畅通工程

    畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  3. 转:并查集总结 例题:hdoj 1232 畅通工程

    引述之类的就免了,我们现在做题碰到的并查集基础题目大都是连通城市(或者村庄学校),接下来我们就称每一个城市为一个元素.我们解决此类题目运用的是树结构,每个集合用一棵树表示,而树的节点用于存储集合中的元 ...

  4. Hdoj 1232.畅通工程 题解

    Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都可以实现交通 ...

  5. 并查集_HDU 1232_畅通工程

    某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可). ...

  6. 傻子都能懂的并查集题解——HDU1232畅通工程

    原题内容: Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府"畅通工程"的目标是使全省任何两个城镇间都 ...

  7. hdu 1232 畅通工程(并查集算法)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1232 畅通工程 Time Limit: 4000/2000 MS (Java/Others)    M ...

  8. HDU 1232 畅通工程(道路连接)(裸并查集)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1232 畅通工程 Time Limit: 4000/2000 MS (Java/Others)     ...

  9. Kruskal HDOJ 1863 畅通工程

    题目传送门 /* 此题为:HDOJ 1233 + HDOJ 1232 */ #include <cstdio> #include <algorithm> #include &l ...

  10. HDU 1232 畅通工程(模板——并查集)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1232 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出 ...

随机推荐

  1. h.SSL协议栈整体分解

    1.SSL整体框图 SSL协议是应用层次(http协议)和TCP层级的一个可选的位置,可以从下面的图中非常清楚看到该层次: 绿色的框图就是这个SSL./TLS的位置,最右面的SSL/TLS图可以进一步 ...

  2. Quartz 基本概念及原理

    最近项目要用quartz,所以记录一下: 概念   Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使 ...

  3. wifidog 配置中文说明

    #网关IDGatewayID default#外部网卡ExternalInterface eth0#无线网卡GatewayInterface eth0#无线IPGatewayAddress 192.1 ...

  4. 编译osg的一个错误

    C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppCommon.targets! 提示找不到CMAKELIST.T ...

  5. HTML5的 2D SVG和SVG DOM的学习笔记(2)---SVG动画

    SVG支持动画.可以通过以下几种方法获得动画效果: 使用SVG动画元素.SVG可以描述随时间变化的图形对象,使用不同的动画元素可以定义运动路径,淡入淡出效果和对象的膨胀.收缩.旋转和变换颜色. 使用S ...

  6. guava学习--FluentIterable

    public class FluentIterableTest { public static void main(String[] args) { Man man1 = new Man(" ...

  7. guacamole 0.8.3 项目部署 桌面虚拟化

    Guacamole是一个基于HTML5的虚拟桌面应用程序,其中包含多个组件,由各组件共同构成Guacamole---一个完整的虚拟桌面解决方案,不需要任何插件,只要浏览器支持HTML5就可以实现,而且 ...

  8. 001 今天开始系统学习C#

    2016-01-16 之前只是大概了解过c#语言,感觉掌握不牢靠.现在开始系统学习C#.现以该博客作为学习笔记,方便后续查看.C# 目标:系统掌握c#知识 时间:30天 范围:C#基础,Winform ...

  9. 【opencv学习笔记】SetImageROI函数设置ROI区域的作用及用法

    虽然先前知道ROI区域是感兴趣区域,但是真正看到调用了OpenCV的cvSetImageROI函数时,并不知道它的作用,所以还是单独写了一段代码对这个函数进行探究.   OpenCVchm文档中对cv ...

  10. Java的内部类

    Java的内部类 首先我们来了解一下什么是内部类? 内部类是指在一个外部类的内部再定义一个类.内部类可以是静态static的,也可用public,default,protected和private修饰 ...