数据结构--并查集

一、概述

1、定义:并查集是一种树型的数据结构

2、应用:用于处理不相交的集合的合并和查询。

二、操作及实现

1、查询:找到自己的老大

cpp 复制代码
int find(int x)
{
    while(f[x]!=x)//如果自己的老大不是自己说明老大另有其人
    {
        x=f[x];//接着往下找老大
    }
    return x;//找到了老大
}

2、合并:合并成一个团伙

cpp 复制代码
void join(int x,int y)
{
    int fx=find[x],fy=find[y];//找到两个人各自的老大
    if(fx!=fy)//如果两个人的老大不一样说明不是一个团伙
    {
        f[fx]=fy;//要合并成一个团伙,只能有一个老大,所以说其中一个老大要认另一个老大做新老大。
    }
}

3、路径压缩:(优化find函数)

如果树的深度过深,查询起来会相当耗时,所以说要减短树的深度。

可以将查询点x到根节点(也就是老大)的途径的点的父节点都设成为根节点,会大大降低查询的难度。

缺点是:只有当找到老大时,才能进行路径压缩,所以每个团体第一次的查询是没有什么优化的,之后才会生效。

cpp 复制代码
int find(int x)//路径压缩 
{
	if(f[x]==x)return x;//如果自己的老大是自己输出
	return f[x]=find(f[x]);//如果不是,继续找老大,并且把自己的父节点设为老大
}

三、模板和例题

1、模板

洛谷P3367

https://www.luogu.com.cn/problem/P3367#submithttps://www.luogu.com.cn/problem/P3367#submit

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int f[N],a,b,c;
int find(int k)
{
	//路径压缩 
	if(f[k]==k)return k;
	return f[k]=find(f[k]);
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		f[i]=i;//初始化老大为自己 
	}
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b>>c;
		if(a==1)
		{
			f[find(b)]=find(c);//合并  c所在组赢了b所在组 
		}
		else
		{
			if(find(b)==find(c))//检查老大是否相同 
			{
				cout<<"Y"<<endl;
			}
			else
			{
				cout<<"N"<<endl;
			}
		}
	}
	return 0;
} 

2、acwing 837、连通块中点的数量

https://www.acwing.com/problem/content/839/

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,f[N],cnt[N];
int find(int x)//不能用路径压缩因为那样的话就没办法统计连通快的数量了
{
    if (f[x] != x) f[x] = find(f[x]);
    return f[x];
}
void join(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(fx!=fy)
    {
        f[fx]=fy;
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
        cnt[i]=1;
    }
    while(m--)
    {
        string flag;
        cin>>flag;
        int a,b;
        if(flag=="C")
        {
            cin>>a>>b;
            if(find(a)==find(b))continue;
            cnt[find(b)]+=cnt[find(a)];//先加连通块数量,否则先操作集合的话会导致重叠。而且要给新老大加,不能加错
            join(a,b);
        }
        else if(flag=="Q1")
        {
            cin>>a>>b;
            if(find(a)==find(b))
            {
                cout<<"Yes"<<endl;
            }
            else
            {
                cout<<"No"<<endl;
            }
        }
        else
        {
            cin>>a;
            cout<<cnt[find(a)]<<endl;
        }
    }
    return 0;
}

3、acwing 240食物链

cpp 复制代码
//把食物环拆成食物链,用某节点到根节点的距离%3的余数来表示各节点之间的关系。
/*
余1:吃根节点
余2:被根节点吃
余0:同类
*/
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int f[N],d[N];
int n,m;
int find(int x)
{
    if(f[x]!=x)//自己不是根节点
    {
        int t=find(f[x]);//先压缩路径使除x外的所有上级都指向根节点并记录根节点是谁
        d[x]+=d[f[x]];//到根节点的距离更新为x到其父节点的距离+父节点到根节点的距离
        f[x]=t;//再让x指向根节点
    }
    return f[x];
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
    }
    int res=0;
    while(m--)
    {
        int t,x,y;
        cin>>t>>x>>y;
        if(x>n||y>n)
        {
            res++;
            continue;
        }
        int fx=find(x),fy=find(y);
        if(t==1)//同类
        {
            //在同一集合中
            if(fx==fy&&(d[x]-d[y])%3)res++;//两者%3不相等,说明不是同一类,谎话++
            else if(fx!=fy)//不在同一集合中,先到者为真
            {
                f[fx]=fy;//合并集合,让其中一个老大认另一个人做老大
                d[fx]=d[y]-d[x];//因为同类,所以(d[x]+d[fx]-d[y])%3==0
            }
        }
        //同上
        else
        {
            if(fx==fy&&(d[x]-d[y]-1)%3)res++;
            else if(fx!=fy)
            {
                f[fx]=fy;
                d[fx]=d[y]+1-d[x];
            }
        }
    }
    cout<<res<<endl;
    return 0;
}
相关推荐
桦说编程3 小时前
如何在Java中实现支持随机访问的固定窗口队列
java·数据结构·后端
一个心烑5 小时前
将一个List分页返回的操作方式
数据结构·list
清辞8535 小时前
C++数据结构(链表和list)
数据结构·c++·链表
澪吟7 小时前
数据结构入门:深入理解顺序表与链表
数据结构·链表
大数据张老师7 小时前
数据结构——直接插入排序
数据结构·算法·排序算法·1024程序员节
给大佬递杯卡布奇诺8 小时前
FFmpeg 基本数据结构 AVPacket分析
数据结构·c++·ffmpeg·音视频
南方的狮子先生9 小时前
【数据结构】从线性表到排序算法详解
开发语言·数据结构·c++·算法·排序算法·1024程序员节
极客智造9 小时前
编程世界的内在逻辑:深入探索数据结构、算法复杂度与抽象数据类型
数据结构·算法·数学建模