并查集

  并查集(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个连通分支,则只需要再修两条路……

  源码如下:

#include<iostream>
using namespace std; const int CMAX = ; // 最多1000个城镇(编号从1开始) int parent[CMAX]; // 存放各结点的直接父节点,对于根结点,parent指向本身
int Rank[CMAX]; // 存放各结点的秩,即该结点在子树中的高度(实际上只有根结点的秩在按秩合并时才有价值) int Find(int x) // 带有路径压缩的查找过程,返回根结点(代表元素)
{
if (x != parent[x])
parent[x] = Find(parent[x]);// 沿着查找路径递归向上直到找到根
return parent[x]; // 找到根,开始回溯,进行路径压缩
} void Union(int x, int y) // 按秩合并两个树(集合),让具有较小秩的根指向具有较大秩的根
{
int root1 = Find(x);
int root2 = Find(y);
if (root1 == root2) // 根结点相同,无需合并
return;
if (Rank[root1] < Rank[root2])
{
parent[root1] = root2;
}
else
{
parent[root2] = root1;
if (Rank[root1] == Rank[root2]) // 只有秩相等时需要递增根的秩(大树合并小树,根秩不变)
Rank[root1]++;
}
} int main()
{
int N, M; // 城镇数目和道路数目
while (scanf("%d%d", &N, &M) && N) // 当N为0时,输入结束
{
int tree_num = ; // 树的个数,即连通分支数
for (int i = ; i <= N; i++) // 初始化每个结点独自成树,秩为1
{
parent[i] = i;
Rank[i] = ;
}
int city1, city2;
for (int i = ; i <= M; i++) // 读取M条道路,合并连通的城镇
{
scanf("%d%d", &city1, &city2);
Union(city1, city2);
}
for (int i = ; i <= N; i++)
{
if (parent[i] == i) // 每找到一个根就有一个连通分支
tree_num++;
}
printf("%d\n", tree_num - ); // 把这些连通分支连起来需要修tree_num-1条路
}
return ;
}

  提交结果:

  参考资料:   《算法导论第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. 数据库 基础篇2(mysql)

    2.1MySQL入门 1)到mysql官网下载. 2)安装mysql软件 3)使用 验证是否成功 打开cmd  -> 输入 mysql -u root -p  回车   -> 输入密码   ...

  2. android studio gradle升级

    http://services.gradle.org/distributions 下载最新的gradle-3.0-all.zip包 放入C:\Users\Administrator\.gradle\w ...

  3. 识别有效的IP地址和掩码并进行分类统

    #include<iostream> #include<stdio.h> #include<string.h> using namespace std; int c ...

  4. sqlite数据类型

    sqlite数据类型(时间 日期 double等)     sqlite3支持的数据类型: NULL.INTEGER.REAL.TEXT.BLOB但是,sqlite3也支持如下的数据类型smallin ...

  5. easy ui datagrid 中getSelections方法只能获取一行数据

    解决方案:设置  idField : "", // 设置标识

  6. 分治法求2n个数的中位数

    问题:设X[0:n-1]和Y[0:n-1]为两个数组,每个数组中含有n个已排好序的数.试设计一个O(logn)时间的分治算法,找出X和Y的2n个数的中位数 思想: 对于数组X[0:n-1]和Y[0:n ...

  7. MySQL与SqlServer中update操作同一个表问题

    一 SqlServer中操作如下图 这个是没问题的. 二 MySQL中操作如下图 但是在MySQL中想实现这个功能如下图,但是出错了. 原来是MySQL中不支持子查询的 我们可以这样修改一下就可以实现 ...

  8. 上下文菜单项(contextMenu)----长按按钮弹出菜单项

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  9. CSS样式选择器优先级

    CSS样式选择器分为4个等级,a.b.c.d,可以以这四种等级为依据确定CSS选择器的优先级. 1.如果样式是行内样式(通过Style=””定义),那么a=12.b为ID选择器的总数3.c为Class ...

  10. MyJni

    package com.baidu.jnitest; import android.os.Bundle; import android.app.Activity; import android.vie ...