算法基础课—数据结构(六) 并查集

基本原理与相关操作

在这里插入图片描述

时间复杂度O(1)

基本原理:用树来维护集合

根节点的编号就是当前集合的编号
如果想知道每个元素属于哪个集合——找父节点直到根节点

代码的结构

  1. 一个一维数组p[N],用于存储其父节点
  2. 初始:所有p[N]指向自身,一个点为一个集合
  3. 集合合并:改变根节点的父节点
  4. 集合查询,找到根节点的位置

进一步优化——路径压缩

走第一遍后就把所有的都指向父节点
在这里插入图片描述

代码

所有p[x] 最后都指向祖宗节点

    // 返回x的祖宗节点
    int find(int x)
    {
   
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

合并集合

题目

一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。

现在要进行 m 个操作,操作共有两种:

M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m。

接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。

输出格式
对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。

每个结果占一行。

数据范围
1≤n,m≤105
输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes

算法思想

用树来表示集合,集合的合并,就是树的合并——这样合并和查找操作都较为简单
一棵树即是一个集合

普通做法:
用p[N]来表示其父节点,其根节点就是集合编号,判断是否到根节点就是p[x] == x
优化做法:
遍历一次后,每个p[N]都直接指向其所在集合编号,省略向上遍历的过程。

初始条件:一开始的时候,每个数都为一个集合,p[x] = x。
集合合并:则其增加父节点,指向新的集合编号
集合查询,判断两个集合编号是否相等。

p[N]本质上是指向集合编号的数组,由于集合之间可能存在嵌套,所以需要向上遍历到最高点。

集合合并也是两个集合的合并,要找到其所属的最大集合进行合并。
优化就是省略了后续遍历的重复

代码

#include <iostream>
using namespace std;
const int N = 2e6;
int p[N];
int find(int x){
   
    if(p[x] != x) 
        p[x] = find(p[x]);
    
    return p[x];//返回祖宗节点,同时也让期间的所有点都指向祖宗节点
}
int main(){
   
    int n,m,i;
    int a,b;
    char op[2];
    cin>>n>>m;
    for(i = 1;i <= n; i ++) p[i] = i;
    for(i = 0; i < m; i ++){
   
        cin>>op>>a>>b;
        if(op[0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值