对于一个集合常见的操作有:判断一个元素是否属于一个集合;合并两个集合等等。而并查集是处理一些不相交集合(Disjoint Sets)的合并及查询问题的有利工具。

  并查集是利用树结构实现的。一个集合用一棵树来表示,而多个集合便是森林。并查集中的“并”是将两个集合合并即两棵树合并成一颗树;“查”是查找一个元素属于哪个集合,即查找一个节点属于哪棵树。思路如下:

  • 查:通过节点找寻父节点,一直向上查找直到根节点,返回根节点,而根节点代表唯一的那棵树;
  • 并:先查找到两个节点所在的树,如果在同一棵树中(即查找到的根节点相同),则直接返回;否则将一棵树作为子树连到另一棵树上,即将一个根节点作为另一个根节点的儿子。

  那么问题来了,应该将哪个根节点作为儿子呢?简单的想法是随便都可以,所以在编程实现中,要么一直让前一个根节点作为儿子,要么一直让后一个根节点作为儿子。这种实现的优点是实现简单,代码简洁短小。但是要知道相对短的代码不一定效率高。在这个问题上,这种做法容易让树失去平衡,因为可能会将较大树作为较小树的子树,使树的深度增大。事实上,更自然的想法是将较小树作为较大树的子树,这样合并操作不会增加树的深度(当然如果两棵树同样大一定会增加),使树相对平衡。

  集合没有精确定义,通常把一些互不相同的东西放在一起所形成的整体就叫做集合。至于为什么放在一起,是根据具体问题的需求,比如把有公路相连的城镇放在一个集合中(连通性问题)。确定性,互异性,无序性是集合的三大性质。由于集合的性质,通常可以和自然数一一对应。因此,用数组实现并查集非常方便且巧妙:数组下标(从1开始)作为树节点,数组下标对应的值作为父节点。由于根节点没有父节点,故不妨令它对应数组的值为非正数。而如何比较两棵树谁大谁小,一个非常巧妙的技巧是让根节点对应的数组的值(非正数)的绝对值作为这棵树的深度。这样做代码不仅简洁,而且节省空间。具体实现的代码如下:

int father[N] = {};      //初始化,所有节点都是根节点,深度为0;N为最大可能节点数
int n; //n为实际节点数
/*查找函数*/
int Find(int father[], int n, int x)
{
int root;
for (root = x; father[root] > ; root = father[root]); //向上遍历直到根节点退出循环
return root; //返回根节点
}
/*合并函数*/
void Union(int father[], int n, int a, int b)
{
int ARoot, BRoot;
ARoot = Find(father, n, a); //找到a节点所在树的根节点
BRoot = Find(father, n, b); //找到b节点所在树的根节点
if (ARoot == BRoot)
return;
else if (father[ARoot] > father[BRoot]) //B树深度大于A树
{
/*树的深度不变*/
father[ARoot] = BRoot; //将A树指向B树
}
else
{
if (father[ARoot] == father[BRoot])//A,B两棵树深度相同
father[ARoot]--; //树的深度加1,根节点对应数组的值(非正数)的绝对值为这棵树的深度
father[BRoot] = ARoot;
}
}

下面是一个考察并查集的练习,题目来源:http://www.patest.cn/contests/mooc-ds/04-%E6%A0%915

We have a network of computers and a list of bi-directional connections. Each of these connections allows a file transfer from one computer to another. Is it possible to send a file from any computer on the network to any other?

Input Specification:

Each input file contains one test case. For each test case, the first line contains N (2<=N<=104), the total number of computers in a network. Each computer in the network is then represented by a positive integer between 1 and N. Then in the following lines, the input is given in the format:

I c1 c2

where I stands for inputting a connection between c1 and c2; or

C c1 c2

where C stands for checking if it is possible to transfer files between c1 and c2; or

S

where S stands for stopping this case.

Output Specification:

For each C case, print in one line the word "yes" or "no" if it is possible or impossible to transfer files between c1 and c2, respectively. At the end of each case, print in one line "The network is connected." if there is a path between any pair of computers; or "There are k components." where k is the number of connected components in this network.

Sample Input 1:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
S

Sample Output 1:

no
no
yes
There are 2 components.

Sample Input 2:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
I 1 3
C 1 5
S

Sample Output 2:

no
no
yes
yes
The network is connected. 代码如下:
#include <cstdio>
#include <cstring> #define N 10010 /*查找函数: 通过节点的值查找其所在树的根节点*/
int Find(int father[], int n, int x);
/*合并函数: 合并两个节点*/
void Union(int father[], int n, int a, int b); int main()
{
char operation[];
int father[N] = {}; //初始化,所有节点都是根节点,深度为0
int n;
int a, b; scanf("%d", &n);
while (scanf("%s", operation) == )
{
if (strcmp(operation, "S") == ) break;
if (strcmp(operation, "C") == )
{
scanf("%d%d", &a, &b);
if (Find(father, n, a) == Find(father, n, b)) //若根节点相同则在同一颗树上
printf("yes\n");
else
printf("no\n");
}
else
{
scanf("%d%d", &a, &b);
Union(father, n, a, b);
}
} int k = ; //统计根节点的个数(树的个数)
for (int i = ; i <= n; i++)
{
if (father[i] <= )
k++;
}
if (k == ) //只有一棵树,即全部连通
printf("The network is connected.\n");
else
printf("There are %d components.\n", k); return ;
} void Union(int father[], int n, int a, int b)
{
int ARoot, BRoot;
ARoot = Find(father, n, a); //找到a节点所在树的根节点
BRoot = Find(father, n, b); //找到b节点所在树的根节点
if (ARoot == BRoot)
return;
else if (father[ARoot] > father[BRoot]) //B树深度大于A树
{
/*树的深度不变*/
father[ARoot] = BRoot; //将A树指向B树
}
else
{
if (father[ARoot] == father[BRoot]) //两树深度相等
father[ARoot]--; //树的深度加1,根节点对应数组的值(非正数)的绝对值为这棵树的深度
father[BRoot] = ARoot;
}
} int Find(int father[], int n, int x)
{
int root;
for (root = x; father[root] > ; root = father[root]); //向上遍历直到根节点退出循环
return root; //返回根节点
}

图解样例2如下:



04-树5. File Transfer--并查集的更多相关文章

  1. File Transfer(并查集)

    题目大意:将多个电脑通过网线连接起来,不断查询2台电脑之间是否连通. 问题来源:中国大学mooc 05-树8 File Transfer (25 分) We have a network of com ...

  2. 【CF576E】Painting Edges 线段树按时间分治+并查集

    [CF576E]Painting Edges 题意:给你一张n个点,m条边的无向图,每条边是k种颜色中的一种,满足所有颜色相同的边内部形成一个二分图.有q个询问,每次询问给出a,b代表将编号为a的边染 ...

  3. 洛谷P4145 上帝造题的七分钟2/花神游历各国 [树状数组,并查集]

    题目传送门 题目背景 XLk觉得<上帝造题的七分钟>不太过瘾,于是有了第二部. 题目描述 "第一分钟,X说,要有数列,于是便给定了一个正整数数列. 第二分钟,L说,要能修改,于是 ...

  4. BZOJ_4025_二分图_线段树按时间分治+并查集

    BZOJ_4025_二分图_线段树按时间分治+并查集 Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简 ...

  5. Codeforces 1140F Extending Set of Points 线段树 + 按秩合并并查集 (看题解)

    Extending Set of Points 我们能发现, 如果把x轴y轴看成点, 那么答案就是在各个连通块里面的x轴的个数乘以y轴的个数之和. 然后就变成了一个并查集的问题, 但是这个题目里面有撤 ...

  6. NYOJ 129 树的判定 (并查集)

    题目链接 描述 A tree is a well-known data structure that is either empty (null, void, nothing) or is a set ...

  7. BZOJ4399 魔法少女LJJ【线段树合并】【并查集】

    Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了 LJJ感叹道"这里真是个迷人的绿色世界,空气清新.淡雅 ...

  8. 51nod1307(暴力树剖/二分&dfs/并查集)

    题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1307 题意: 中文题诶~ 思路: 解法1:暴力树剖 用一个数 ...

  9. poj 2513 Colored Sticks( 字典树哈希+ 欧拉回路 + 并查集)

    题目:http://poj.org/problem?id=2513 参考博客:http://blog.csdn.net/lyy289065406/article/details/6647445 htt ...

  10. 【BZOJ4025】二分图(线段树分治,并查集)

    [BZOJ4025]二分图(线段树分治,并查集) 题面 BZOJ 题解 是一个二分图,等价于不存在奇环. 那么直接线段树分治,用并查集维护到达根节点的距离,只计算就好了. #include<io ...

随机推荐

  1. MySQL高性能优化实战总结

    1.1 前言 MySQL对于很多Linux从业者而言,是一个非常棘手的问题,多数情况都是因为对数据库出现问题的情况和处理思路不清晰.在进行MySQL的优化之前必须要了解的就是MySQL的查询过程,很多 ...

  2. Jenkins 自动化测试

    学习 Jenkins 自动化测试的系列文章 Robot Framework 概念 Robot Framework 安装 Pycharm + Robot Framework 环境搭建 Robot Fra ...

  3. 复利计算器app发布

    复利计算器app发布 抱歉:由于无法实现服务端的持续开启,发布的app仅为简单的单机版,暂时舍弃了c/s版本的一些功能,如:投资动态管理功能. 应用详情博客:请点击这里 apk下载地址1(百度手机助手 ...

  4. 【探路者】Alpha发布用户使用报告

    预期统计用户使用数量:13人. 博文内容:1用户列表.2评论列表.3统计与总结 1用户列表: 二.评论内容 用户1:1不够好看.2不应该是中国地图为背景,蛇头是人物头像的么?(那是宣传片,不是预览图) ...

  5. huawei oceanstor

      华为产品:OceanStor 6000 V3系列 OceanStor 6800 V3 网页登入设备页面:https+ip+端口 资源分配界面: 首页: wwn为2100xxxxxxxx47e4,设 ...

  6. 《高性能JavaScript》学习笔记(2)——日更中

    我说日更就日更,接着....今天从缓冲布局信息开始啦! -------------------2016-7-22 21:09:12------------------------------- 14. ...

  7. php伪静态配置

    配置虚拟主机和伪静态 1.开启Apache的rewrite模块 LoadModule rewrite_module modules/mod_rewrite.so 2.开启虚拟主机功能 # Virtua ...

  8. gulp 定义依赖关系

    var gulp = require('gulp'); // 返回一个 callback,因此系统可以知道它什么时候完成 gulp.task('one', function(cb) { // 做一些事 ...

  9. elasticsearch6 学习之基础CURD

    环境:elasticsearch6.1.2        kibana6.1.2  基础概念: 1._index元数据 (1)代表一个document存放在哪个index中(2)类似的数据放在一个索引 ...

  10. [LeetCode] PathSum

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...