前言

在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

这一类问题其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

定义

并查集(Disjoint Set),即“不相交集合”,是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

将编号分别为1…N的N个对象划分为不相交集合,在每个集合中,选择其中某个元素代表所在集合。

常见两种操作:

  • 合并两个集合
  • 查找某元素属于哪个集合

用编号最小的元素标记所在集合;定义一个数组set[1...n],其中set[i]表示元素i 所在的集合;

算法实现

查找

时间复杂度:\(O(1)\)

find1(x)
{
return set[x];
}

合并

时间复杂度:\(O(n)\)

Merge1(a,b)
{
i = min(a,b);
j = max(a,b);
for (k = 1; k <= N; k++) {
if (set[k] == j)
set[k] = i;
}
}

对于合并操作,必须搜索全部元素!有没有可以改进的地方呢?

算法的优化

使用树结构

每个集合用一棵“有根树”表示,定义数组set[1...n]

  • set[i] = i,则 i 表示本集合,并且是集合所对应树的根
  • set[i] = j,j<>i,则 j 是 i 的父节点

查找

时间复杂度(最坏):\(O(n)\)

find2(x)
{
r = x;
while (set[r] != r)
r = set[r];
return r;
}

合并

时间复杂度:\(O(1)\)

merge2(a, b)
{
if (a<b)
set[b] = a;
else
set[a] = b;
}

避免最坏情况

方法:将深度小的树合并到深度大的树

实现:假设两棵树的深度分别为h1和h2, 合并后的树的高度为h,则

\[h =
\begin{cases}
max(h1, h2), & \text{if h1<>h2} \\
h1+1, & \text{if h1=h2}
\end{cases}
\]

效果:任意顺序的合并操作以后,包含k个节点的树的最大高度不超过\(\log_2{k}\)

查找

时间复杂度:\(O(\log_2{n})\)

find2(x)
{
r = x;
while (set[r] != r)
r = set[r];
return r;
}

合并

时间复杂度:\(O(1)\)

merge3(a,b)
{
if (height(a) == height(b)) {
height(a) = height(a) + 1;
set[b] = a;
} else if (height(a) < height(b)) {
set[a] = b;
} else {
set[b] = a;
}
}

路径压缩

思想:每次查找的时候,如果路径较长,则修改信息,以便下次查找的时候速度更快。

步骤:

  1. 找到根结点
  2. 修改查找路径上的所有节点,将它们都指向根结点

路径压缩示意图:

查找

find3(x)
{
r = x;
while (set[r] != r) //循环结束,则找到根节点
r = set[r];
i = x;
while (i != r) //本循环修改查找路径中所有节点
{
j = set[i];
set[i] = r;
i = j;
}
}

hdu1232

#include<stdio.h>
int x[1005];
int min(int a,int b);
int max(int a,int b);
void xs(int a,int b);
int fine(int a);
int main()
{
int n,m,i,a,b;
while(scanf("%d",&n)&&n)
{
int sum = -1;
scanf("%d",&m);
for(i=1;i<=n;i++) x[i]=i; //首先把各自的父节点设为自身
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
xs(a,b); //合并两个集合
}
for(i=1;i<=n;i++)
{
if(x[i]==i) sum++; //算出(最后不同集合的个数-1)即为所求
}
printf("%d\n",sum);
}
return 0;
}
int min(int a,int b)
{
return a<b ? a : b;
}
int max(int a,int b)
{
return a>b ? a : b;
}
int fine(int a)
{
if(x[a]==a) return a;
else return fine(x[a]);
}
void xs(int a,int b)
{
x[max(fine(a),fine(b))] = min(fine(a),fine(b));
}

hdu1232 并查集总结的更多相关文章

  1. hdu1232 并查集

    1. hdu1232 2.链接:http://acm.hdu.edu.cn/showproblem.php?pid=1232 3.总结:简单并查集 #include<iostream> # ...

  2. 畅通工程--hdu1232(并查集)

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

  3. 并查集入门(hdu1232“畅通工程”)

    在学习并查集之前,首先需要明白基本的并查集可以完成的功能.并查集主要是用于处理不相交集合的合并问题.它是一种基础算法,在离散数学中,可以利用并查集求一个图的连通分支,利用其这个特性可以为我们解决一系列 ...

  4. hdu1232 城镇间修路(并查集)

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

  5. HDU1232 畅通工程 并查集

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

  6. 【HDU1232】畅通工程(并查集基础题)

    裸敲并查集,很水一次AC #include <iostream> #include <cstring> #include <cstdlib> #include &l ...

  7. [HDU1232] 畅通工程 (并查集 or 连通分量)

    Input 测试输入包含若干测试用例.每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M:随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的 ...

  8. 利用并查集+贪心解决 Hdu1232

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

  9. HDU1232——畅通工程【并查集】

    <题目链接> 题目大意: 利用并查集找出图中有几个不连通的城镇集合,所需修的道路数即为城镇集合-1. #include <stdio.h> ]; int find(int x) ...

随机推荐

  1. 『动善时』JMeter基础 — 16、JMeter配置元件【HTTP信息头管理器】

    目录 1.用于演示的项目说明 2.测试计划内包含的元件 3.HTTP请求界面内容 4.查看脚本执行结果 5.添加请求头信息(HTTP信息头管理器) 6.优先级说明 7.补充:常见请求头信息 JMete ...

  2. 虚拟机快速下载安装配置aarch64-linux-gnu-gcc工具链

    方式一:软件仓库安装 此方法不用自己去配置交叉编译工具链 1.查看本地仓库有支持哪些版本哪些 输入命令: apt-cache search aarch64 2.下载安装 gcc-8-aarch64-l ...

  3. [刷题] PTA 7-64 最长对称子串

    7-64 最长对称子串 我的代码: 1 #include<stdio.h> 2 #include<string.h> 3 #define N 1001 4 5 int main ...

  4. [刷题] 112 Path Sum

    要求 给出一个二叉树及数字sum,判断是否存在一条从根到叶子的路径,路径上的所有节点和为sum 实现 转化为寻找左右子树上和为 sum-root 的路径,到达叶子节点时递归终止 注意只有一个孩子时,根 ...

  5. mysqldMY-010457] [Server] --initialize specified but the data directory has files in it. Aborting. 2020-12

    删除mysql的/var/lib/mysql目录下的所有文件 https://blog.csdn.net/tr1912/article/details/81271851 # mysqld --init ...

  6. date命令月日时分年

    # date +%Y/%m/%d2019/09/29[root@a-3e5 lpts-20190910-keyan-v0.2]# date +%H:%M20:00

  7. ssh判断免密登陆

    ssh判断免密登陆 [root@jenkins ~]# vi /opt/release_code.sh #!/bin/bash . /etc/init.d/functions #echo $WORKS ...

  8. Linux中级之ansible概念及hoc命令行调用模式

    一.Ansible简介 ansible是新出现的开源的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric)的优点,实现了批量系统 ...

  9. rpm命令的简介(2)-(转自 青春乐园 )

    青春乐园 怎样查看rpm安装包的安装路径 rpm -qpl xxxxxx.rpm 1.如何安装rpm软件包 rmp软件包的安装可以使用程序rpm来完成.执行下面的命令 rpm -i your-pack ...

  10. easyUI中datagrid展示对象下属性以及显示多个子属性(Day_37)

    easyUI中datagrid展示对象下属性以及显示多个子属性 显示对象单个属性值 添加formatter属性 <th field="decidedzone" width=& ...