数据结构之并查集

并查集

更加详细的介绍参见:并查集介绍

基础知识

最基本的并查集定义及操作

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 最长连续序列

leetcode 128 hard
在这里插入图片描述

对于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 被围绕的区域

leetcode130
在这里插入图片描述

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;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值