【算法重修】并查集(带权+路径压缩)

算法思想

并查集(Union-Find)本质上是解决动态连通性(Dynamic-Connectivity)问题
一般包括以下操作:

  • 合并(Union):将两个节点连通
  • 查询(Find):询问两个节点间是否存在通路

数据结构

采用树形结构,但以下部分用数组实现

并查集类(UnionFind)类设计

  • 数组存储id[i]表示父节点,id[i]若为i本身则为根节点
  • 权值存储sz[i]表示树的规模,初始化为1

路径压缩

每次找根节点时将路径上的父节点向根节点上提,可以保证树的高度≤3(几乎展平)

代码实现

/*
Starting from an empty data structure,
any sequence of M union-find ops on N objects
makes ≤c(N + M lg*N) array accesses
·Analysis can be improved to N + M α(M,N) 
·lg* stands for iterate log function, ≈5
*/
class UnionFind{
	private:
		int *id, *sz;//sz[i]:以i为根的树高 
		
		int root(int i){
			while(i!=id[i]){
				id[i]=id[id[i]];//将树展平 
				i=id[i];
			}
			return i;
		}
		
	public:
		UnionFind(int N){
			id=new int[N],sz=new int[N];
			for(int i=0; i<N; i++){
				id[i]=i;
				sz[i]=1;
			}
		}
		
		bool isConnected(int p,int q){
			return root(p)==root(q);
		}
		
		void quickUnion(int p,int q){
			int i=root(p);
			int j=root(q);
			if (i==j) return;
			if (sz[i]<sz[j]) { id[i]=j; sz[j]+=sz[i]; }
			else			 { id[j]=i; sz[i]+=sz[j]; }
			//小树根节点连接到大树根节点上 
		}
};

测试代码

可以用一下主程序测试 (或者打模板题)

int main(){
	int n,m,p,x,y;
	cin>>n>>m>>p;
	UnionFind uf=UnionFind(n);
	for(int i=0; i<m; i++){
		cin>>x>>y;
		uf.quickUnion(x-1,y-1);
	}
	for(int i=0; i<p; i++){
		cin>>x>>y;
		cout<<(uf.isConnected(x-1,y-1)?"Yes":"No")<<endl;
	}
	return 0;
}

PS:数组是从0开始编号的,但测试数据是从1开始编号,请根据实际情况改动
在有的题目中,还可以将*sz改为public,比如:洛谷P2078朋友-传送门
为了凑篇幅, 也放在下面仅供参考:

#include <iostream>
using namespace std;

class UnionFind{
	private:
		int *id;
		
		int root(int i){
			while(i!=id[i]){
				id[i]=id[id[i]];
				i=id[i];
			}
			return i;
		}
		
	public:
		int *sz;
		
		UnionFind(int N){
			id=new int[N],sz=new int[N];
			for(int i=0; i<N; i++){
				id[i]=i;
				sz[i]=1;
			}
		}
		
		bool isConnected(int p,int q){
			return root(p)==root(q);
		}
		
		void quickUnion(int p,int q){
			int i=root(p);
			int j=root(q);
			if (i==j) return;
			if (i>j) { id[i]=j; sz[j]+=sz[i]; }
			else	 { id[j]=i; sz[i]+=sz[j]; }
		}
};

int main(){
	int n,m,p,q,x,y;
	cin>>n>>m>>p>>q;
	UnionFind a=UnionFind(n),b=UnionFind(m);
	for(int i=0; i<p; i++){
		cin>>x>>y;
		a.quickUnion(x-1,y-1);
	}
	for(int i=0; i<q; i++){
		cin>>x>>y;
		b.quickUnion(-x-1,-y-1);
	}
	cout<<min(a.sz[0],b.sz[0]);
	return 0;
}

最后! 【算法重修】这个系列虽然很早就开坑了,但是不知道下一次更是什么时候,周更的flag已成月更flag,就随缘吧……

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值