算法:并查集

目录

概念引入

最简单代码 

数据结构

初始化

查询

合并 x 和 y

例题:找亲戚​​​​​​​​​​​​​​

概念引入

· 并查集核心思想:用一个元素代表集合;把集合比喻成帮派,帮主就是代表元素

· 最开始,大家各自为战,帮主就是自己(一个元素的集合,代表就是那个元素自己

· 两个人比武,输方认赢方为帮主(两个元素合并后的集合,代表元素是被认作帮主那个

· 两个帮派比武,输帮帮主带领小弟投降,认赢帮帮主为帮主(两个集合合为一个集合,代表元素是获胜帮的帮主

· 最后这个集合,内部元素的关系是树状结构,以帮主为根节点

· 寻找集合代表元素(寻找根节点)的方法是:不断访问父节点,当父节点是自己时,说明          这是根结点了(树的知识:根结点的父节点是自己)

最简单代码 

数据结构

二维数组 fa[MAX] 一个(MAX的值自己设定)

初始化

有 n 个人, 把二维数组 fa[1] ~ fa[n] 的值设置为下标

查询

对一个数字 x ,返回 x == fa[x] ? x : (fa[x] = find(fa[x]));

合并 x 和 y

有一个比较保险但是有一定时间复杂度的方法,令输入的 x ,y 等于自己查询出来的根结点,比较 x ,y 是否相等(比较是否同根)

如果不同根 ,fa[y] = x ,让一个根结点接入另一个根结点

例题:找亲戚

描述

或许你并不知道,你的某个朋友是你的亲戚。

他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。

如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。

在这种情况下,最好的帮手就是计算机。

为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。

从这些信息中,你可以推出Marry和Ben是亲戚。

请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。

输入 

输入由两部分组成。

第一部分以N,M开始。N为问题涉及的人的个数。这些人的编号为1,2,3,…,N。下面有M行,每行有两个数ai,bi,表示已知ai和bi是亲戚。

第二部分以Q开始。以下Q行有Q个询问,每行为ci,di,表示询问ci和di是否为亲戚。

数据范围

1≤N≤20000
1≤M≤10^6,
1≤Q≤10^6

输出 

对于每个询问ci,di,输出一行:若ci和di为亲戚,则输出“Yes”,否则输出“No”。

输入样例 1 

10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9

输出样例 1

Yes
No
Yes
#include<iostream>

using namespace std;

const int MAX = 200000 ;
int fa[MAX] ;
int find_fa(int per1){
    return per1 == fa[per1] ? per1 : (fa[per1] = find_fa(fa[per1]));
}
int together(int per1 , int per2){
    per1 = find_fa(per1) ;
    per2 = find_fa(per2) ;
  	if(per1 != per2){
    	fa[per2] = per1 ;
    }//不知道为什么加了这个判定是否是相同根的代码就过了啊。。。。
    return  0 ;
}
int init(){
    int persons = 0,times = 0;
    scanf("%d%d" , &persons , &times) ;
    for(int i = 1 ; i <= persons ; i++){
        fa[i] = i ;
    }
    for(int i = 0 ; i < times ; i++){
        int per1 = 0, per2 = 0;
        scanf("%d%d" , &per1 ,&per2);
        together(per1 , per2) ;
    }
    return 0;
}

int ask(){
    int times = 0 ;
    scanf("%d" ,&times);
    for(int i = 0 ; i < times ; i++){
        int per1 = 0, per2 = 0;
        scanf("%d%d" , &per1 ,&per2) ;
        if(find_fa(per1) == find_fa(per2)){
            cout << "Yes" << endl ;
        }
        else{
            cout << "No" << endl ;
        }
    }
    return  0;
}
int main(){
    init() ;
    ask() ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值