DisJoint_Set

本文介绍了使用自定义Disjoint_Set结构实现不相交集(并查集)的基本操作,如插入、查找、并集合并,并展示了如何在实际问题中应用这一数据结构。通过实例展示了如何通过映射关系管理和路径压缩来优化查找效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//不相交集的概念(并查集) Union Find 
//disjoint set &&  set
//查找某个元素在那个集合 
//map <A B >  key - value
#include<iostream>
#include<set>
#include<map>
using namespace std;
typedef struct Disjoint_Set{
	int *data;
	int *parent;//负数利用起来 
	int capacity;
	int size;
	map<int,int>m;//
	//构造函数 
	Disjoint_Set(int max=10){
		capacity=max;
		size=0;
		//从1开始放元素 
		parent=new int[max+1];
		data=new int[max+1];
	}
	//析构函数
	~ Disjoint_Set(){
		delete[]parent;
		delete[] data;
	}
	bool insert(int x){
		if(size==capacity) return false;
		size++;
		data[size]=x;
		m[x]=size;//看成 
		parent[size]=-1;//改成表示大小为1 
		
		
		return true;
	}
	void print(){
		for(int i=1;i<=size;i++){
			cout<<i<<"\t";		
		}
		cout<<endl;
		for(int i=1;i<=size;i++){
			cout<<parent[i]<<"\t";		
		}
		cout<<endl;
		for(int i=1;i<=size;i++){
			cout<<data[i]<<"\t";		
		}
		cout<<endl;
	}
	//找x的树根的下标是多少 
	int find(int x){
		typename map<int,int>::iterator it;
		it=m.find(x);
		if(it==m.end()) return -1;
		//否则找到的话,it指向的是找到的那个键值对,不只是data 
		int i,rt;
		i=rt=it->second;
		while(parent[rt]>0)
			rt=parent[rt];
		
//路径压缩 
		int tmp;//临时变量	
		for(;i!=rt;i=tmp){
			tmp=parent[i];
			parent[i]=rt;
		}	
			
			
	
		return rt;
	} 
	//并两个数,不止是树根 
	void unionset(int x,int y){
		int rx,ry;
		rx=find(x);//树根的size 
		ry=find(y);
		
		if(rx==-1||ry==-1) return ;
		if(rx==ry) return ;
		if(parent[rx]<parent[ry]){
			parent[rx]+=parent[ry];
			parent[ry]=rx;
		}
		else{
			parent[ry]+=parent[rx];
			parent[rx]=ry;
		}	
	}
	
	
}Disjoint_Set;

int main(){
	Disjoint_Set s; 
	s.insert(11);
	s.insert(22);
	s.insert(66);
	s.insert(-5);
	s.insert(123);
	
	s.unionset(11,66);
	s.unionset(22,11);
//	s.unionset(66,-5);    
	s.print() ;

//	数据->下标->父亲  返回下标 
	 
	
	
	
	
	return 0; 
} 

下面两道题,开始是用上面自己写的代码套进去,发现不对
再看了一下题目发现 它没有插入操作,直接用数组把 size 和 data[size] 合到一起了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e7+10;
int vis[maxn];
void init(){
	for(int i=1;i<=maxn;i++){
		vis[i]=i;
	}
}
int find(int x)
{
	if(vis[x]==x){
		return x;
	}
	else{
		vis[x]=find(vis[x]);
		return vis[x];
	}
}
void merge(int a,int b)
{
	int p=find(a);
	int q=find(b);
	if(p==q){
		return ;
	}
	else{
		if(q>p) 
			vis[q]=p;
		else
			vis[p]=q;
		return ;
	}
	return;
}
int main()
{
	int n;
	cin>>n;
	init();
	while(n--){
		int a,b,c;
		scanf("%d %d %d",&a,&b,&c);
		if(a==1){
			merge(b,c);
		}
		else if(a==2){
			if(find(b)==find(c)){
				printf("YES\n");
			}
			else{
				printf("NO\n");
			}
		}
	}
	return 0;
}

注意题目在中说的是,从1开始连续,所以在判断sum的时候,比较最大值是sum就🆗了,注意循环赋值不要错了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e4+10;
int vis[maxn];
int fri_ans=0,sum=0,quan_ans=0;
void init(){
	for(int i=1;i<=maxn;i++){
		vis[i]=i;
	}
}
int find(int x)
{
	if(vis[x]==x){
		return x;
	}
	else{
		vis[x]=find(vis[x]);
		return vis[x];
	}
}
void merge(int a,int b)
{
	int p=find(a);
	int q=find(b);
	if(p==q){
		return ;
	}
	else{
		if(q>p) 
			vis[q]=p;
		else
			vis[p]=q;
		
		fri_ans++; 
		return ;
	}
}
int main()
{
	int n;
	cin>>n;
	init();
	while(n--){
		int i,a,b,c;
		cin>>a>>b;
		if(b>sum) sum=b;//因为不止一次,所以要判断看看要不要赋值
		for(i=0;i<a-1;i++){
			cin>>c;
			if(c>sum) sum=c;//
			merge(b,c);
		}
	}
	
	//对于基友圈的理解,总人数-好朋友的次数 
	quan_ans=sum-fri_ans;
	cout<<quan_ans<<" "<<sum<<endl;
	int k;cin>>k;
	while(k--){
		int a,b;
		cin>>a>>b;
		if(find(a)==find(b)) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值