算法学习笔记(1)并查集

前几次发了3篇文章,练练手。今天才是正式接触博客园。

由于培训班进度有点慢,也是为了打好基础,今天开始正式自学。

今天学并查集:

(1)并查集是什么?

说白了,并查集是一种数据结构,有以下功能。

1.检查a和b是否为一组。(same操作)

2.合并a与b的组。(unite操作)

比如:

此时的并查集每个点都是单独的一组。

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

然后,合并了1和3所在的一组。

【13】 2 4 5 6 ......

合并2和4所在的组。

【1,3】 【2,4】 5 6 7 ......

合并1和4所在的组(这里的1的所在的组并不是1单个数,是【1,3】这个大组)

【1,2,3,4】 5 6 7 ......

 查询1和2是否在一组。返回1。

(2)并查集的本质

其实,并查集的大概思路就是对于每一组,选出一个老大,查询时,看看两个数的老大是否相同就可以了。合并时,让一个老大臣服于另一个老大,这样,两个组的人都有共同的老大了。

那具体如何实现呢?

一个组可以理解成一棵树,那老大呢?理解成跟就可以了。

(3)并查集的实现。

由于是树,会有退化这种情况,需要避免。(让高度大的树的根成为高度小的树的根的父亲,有效避免退化)这是第一个优化的地方。

第二个,由于每次合并,需要查一下根,下一次又查一下根。很浪费时间,只要让求好根的点直接连向它的根即可。(称为路径压缩)。

加入这两种优化后并查集效率极高,所有操作都是O(a(n)),a(n)这个函数比logn还快!!。

以下是一种实现方法:

#include <bits/stdc++.h>
#define ll long long
#define MP make_pair
#define pa pair<int,int>
#define fift_que pa,vector<pa >,greater<pa >
#define p_qw priority_queue<fift_que >
using namespace std;
int a,b;

typedef unsigned long long ull;
typedef unsigned int ui;
const int N=1e6+1;
//并查集 
int dad[1000006];//节点的父亲 
int rank[1000006];//树高 
void init(int n){
    memset(rank,0,sizeof(rank));
	for(int i=0;i<=n;i++)dad[i]=i;
}//初始化 
int find(int x){
    //找根
	if(dad[x]==x)return x;
    else return dad[x]=find(dad[x]);//路径压缩 
}
void unite(int x,int y){//合并
	int lx=find(x),ly=find(y);
	if(rank[lx]<rank[ly])dad[lx]=ly;
	else{//避免退化 
		dad[ly]=lx;
		if(rank[lx]==rank[ly])rank[lx]++;
	}
}//查询
bool same(int x,int y){
    return find(x)==find(y);
}



int main(){
	init(N);
	//1 2 3 4 5 6 7 8 9......
	unite(1,2); 
	//[1,2] 3 4 5 6 7 8 9......
	unite(3,4);
	//[1,2] [3,4] 5 6 7 8 9......
	cout<<same(1,4)<<" ";
	//1---[1,2] 无4 返回0
	unite(2,3);
	//[1,2,3,4] 5 6 7 8 9......
	cout<<same(1,4)<<" ";
	//1---[1,2,3,4] 有4,返回1
	unite(6,9);
	//[1,2,3,4] 5 [6,9] 7 8 ......
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值