并查集
更加详细的介绍参见:并查集介绍
基础知识
最基本的并查集定义及操作
class UnionSet{
public:
UnionSet(int n){
m_n=n;
m_count=n;
father=new int[n];
for(int i=0;i<n;i++){
father[i]=i;
}
}
int find(int x){
return father[x]=(father[x]==x?x:find(father[x]));
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return;
father[fx]=fy;
m_count--;
}
int getcount(){
return m_count;
}
~UnionSet(){
delete []father;
}
private:
int m_n; //并查集的大小
int m_count;//并查集的联通域数量
int *father;//父节点
};
Quick-Find算法
#include<stdio.h>
#include<stdlib.h>
typedef struct UnionSet{
int *color;
int n;
}UnionSet;
UnionSet *init(int n){
UnionSet *u=(UnionSet *)malloc(sizeof(UnionSet));
u->color=(int *)malloc(sizeof(int)*(n+1));//将0号 位置空出来
u->n=n;
for(int i=1;i<=n;i++){
u->color[i]=i;
}
return u;
}
//查找操作
int find(UnionSet *u,int x){
return u->color[x];
}
//合并操作
int merge(UnionSet *u,int a,int b){
if(find(u,a)==find(u,b)) return 0;
int color_a=u->color[a];
for(int i=1;i<=u->n;i++){
//和a的颜色不同,什么都不做直接coninue结束本次循环进入下一次循环
if(u->color[i]!=color_a) continue;
//把所有和a颜色相同的元素的颜色全部改为b的颜色;
u->color[i]=u->color[b];
}
return 1;
}
void clear(UnionSet *u){
if(u==NULL) return ;
free(u->color);
free(u);
return;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
UnionSet *u=init(n);
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
switch(a){
case 1:merge(u,b,c);break;
case 2:
printf("%s\n",find(u,b)==find(u,c)?"Yes":"No");
break;
}
}
clear(u);
return 0;
}
Quick-Union算法
typedef struct UnionSet{
int *father;
int n;
}UnionSet;
UnionSet *init(int n){
UnionSet *u=(UnionSet *)malloc(sizeof(UnionSet));
u->father=(int *)malloc(sizeof(int)*(n+1));//将0好位置空出来
u->n=n;
for(int i=1;i<=n;i++){
u->father[i]=i;
}
return u;
}
//查找操作,递归的找寻公共父节点
int find(UnionSet *u,int x){
if (u->father[x]==x) return x;
return find(u,u->father[x]);
}
//合并操作
int merge(UnionSet *u,int a,int b){
int fa=find(u,a),fb=find(u,b);
if(fa==fb) return 0;
u->father[fa]=fb;
return 1;
}
void clear(UnionSet *u){
if(u==NULL) return ;
free(u->father);
free(u);
return;
}
Weighted-Quick-Union
把节点数量少的接到节点数量多的根节点上。
typedef struct UnionSet{
int *father,*size;
int n;
}UnionSet;
UnionSet *init(int n){
UnionSet *u=(UnionSet *)malloc(sizeof(UnionSet));
u->father=(int *)malloc(sizeof(int)*(n+1));//将0号位置空出来
u->size=(int *)malloc(sizeof(int)*(n+1));
u->n=n;
for(int i=1;i<=n;i++){
u->father[i]=i;
u->size[i]=1;
}
return u;
}
//查找操作
int find(UnionSet *u,int x){
if (u->father[x]==x) return x;
return find(u,u->father[x]);
}
//合并操作
int merge(UnionSet *u,int a,int b){
int fa=find(u,a),fb=find(u,b);
if(fa==fb) return 0;
//fa记录结点数量最多的位置
if(u->size[fa]<u->size[fb]) swap(fa,fb);
u->father[fb]=fa;
u->size[fa]+=u->size[fb];
return 1;
}
void clear(UnionSet *u){
if(u==NULL) return ;
free(u->father);
free(u->size);
free(u);
return;
}
Weighted-Quick-Union with Compression
typedef struct UnionSet{
int *father,*size;
int n;
}UnionSet;
UnionSet *init(int n){
UnionSet *u=(UnionSet *)malloc(sizeof(UnionSet));
u->father=(int *)malloc(sizeof(int)*(n+1));//将0好位置空出来
u->size=(int *)malloc(sizeof(int)*(n+1));
u->n=n;
for(int i=1;i<=n;i++){
u->father[i]=i;
u->size[i]=1;
}
return u;
}
//查找操作
int find(UnionSet *u,int x){
if (u->father[x]==x) return x;
//路径压缩更改此位置,每次都是最顶层的根节点
return u->father[x]=find(u,u->father[x]);
}
//合并操作
int merge(UnionSet *u,int a,int b){
int fa=find(u,a),fb=find(u,b);
if(fa==fb) return 0;
if(u->size[fa]<u->size[fb]) swap(fa,fb);
u->father[fb]=fa;
u->size[fa]+=u->size[fb];
return 1;
}
void clear(UnionSet *u){
if(u==NULL) return ;
free(u->father);
free(u->size);
free(u);
return;
}
练习题
128 最长连续序列
对于unordered_map,底层实现为哈希表,其中:
使用count,返回的是被查找元素的个数。如果有,返回1;否则,返回0。注意,map中不存在相同元素,所以返回值只能是1或0。使用find,返回的是被查找元素的位置,没有则返回map.end()。
class Solution {
public:
unordered_map<int,int>uf,cnt;
//并查集的find操作,其中采用了压缩路径
int find(int x){
return uf[x]=(uf[x]==x?x:find(uf[x]));
}
//并查集的union操作,其中weighted-union;
int merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return cnt[fx];
//把较小数量的树接在较大数量的树下
if(cnt[fx]<cnt[fy]) swap(cnt[fx],cnt[fy]);
uf[fy]=fx;
cnt[fx]+=cnt[fy];
return cnt[fx];
}
int longestConsecutive(vector<int>& nums) {
if(nums.size()==0) return 0;
int res=1;
//初始化并查集的father和size;
for(int i:nums){
uf[i]=i;
cnt[i]=1;
}
for(int i:nums){
//如果i+1存在,合并,并返回较大的子树数量
if(uf.count(i+1)){
res=max(res,merge(i,i+1));
}
}
return res;
}
};
130 被围绕的区域
class UnionSet{
public:
UnionSet(int n){
this->m_n=n;
this->father=new int[n];
for(int i=0;i<n;i++){
father[i]=i;
}
}
int find(int x){
//采用路径压缩可以减少时间
return father[x]=(father[x]==x?x:find(father[x]));
//if(father[x]==x) return x;
//return find(father[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return;
father[fx]=fy;
}
bool isconnect(int x,int y){
return find(x)==find(y);
}
~UnionSet(){
delete[]father;
}
private:
int *father;
int m_n;
};
class Solution {
public:
int node(int x,int y,int j){
return x*j+y;
}
int dir[2][2]={0,1,1,0};
void solve(vector<vector<char>>& board) {
if(board.size()==0||board[0].size()==0) return ;
//初始化并查集的大小为N+1;下标元素为0-N;
UnionSet u=UnionSet(board.size()*board[0].size()+1);
//设置虚拟父节点为N;将边界上为O的点与虚拟父节点联通
int dummpy_fa=board.size()*board[0].size();
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(board[i][j]!='O') continue;
bool edge=i==0||i==board.size()-1||j==0||j==board[0].size()-1;
for(int k=0;k<2;k++){
int xx=i+dir[k][0];
int yy=j+dir[k][1];
if(xx<board.size()&&yy<board[0].size()&&board[xx][yy]=='O'){
u.merge(node(xx,yy,board[0].size()),node(i,j,board[0].size()));
}
}
if(!edge) continue;
u.merge(node(i,j,board[0].size()),dummpy_fa);
}
}
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(board[i][j]!='O') continue;
if(!u.isconnect(dummpy_fa,node(i,j,board[0].size()))){
board[i][j]='X';
}
}
}
}
};
200 岛屿数量
class UnionSet{
public:
UnionSet(int n){
m_n=n;
m_count=n;
father=new int[n];
for(int i=0;i<n;i++){
father[i]=i;
}
}
int find(int x){
return father[x]=(father[x]==x?x:find(father[x]));
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return;
father[fx]=fy;
m_count--;
}
int getcout(){
return m_count;
}
~UnionSet(){
delete []father;
}
private:
int m_n;
int m_count;
int *father;
};
class Solution {
public:
int node(int x,int y,int cols){
return x*cols+y;
}
int dir[2][2]={0,1,1,0};
int numIslands(vector<vector<char>>& grid) {
if(grid.size()==0||grid[0].size()==0) return 0 ;
int rows=grid.size(),cols=grid[0].size();
int zero_num=0;
UnionSet u=UnionSet(rows*cols);
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
if(grid[i][j]=='0'){
zero_num++;
}else{
for(int k=0;k<2;k++){
int xx=i+dir[k][0];
int yy=j+dir[k][1];
if(xx<rows&&yy<cols&&grid[xx][yy]=='1')
u.merge(node(i,j,cols),node(xx,yy,cols));
}
}
}
}
return u.getcout()-zero_num;
}
};