【数据结构】并查集

 【并查集】

      为实现 在 不相交集合 上的操作 (1.合并两个集合  2.查询某个元素属于哪个集合)而定义的一种数据结构

        其实现有两种方式:链表和有根树

    

 

【应用】

    在图论中 一个联通分量的所有点 对应一个集合

    对应的操作可以为

      判断两个点是不是在同一个联通分量之中

      添加一条边合并两个联通分量

优化】 

  (1)路径压缩(优化查找操作)

       (2)通俗点说法就是要合并两个树,将树高度低的接到 高度高的树下, 使合并后的树的高度尽量小 (优化合并操作)

 

【模板】
    
此处用树来实现 用数组储存

/*
    并查集模板
    by chsobin 
*/
int const maxn = 1000000;
int fa[maxn];                //father
void init(int n){
    for(int i=0;i<=n;++i){
        fa[i] = i;            //初始时候设置每个集合只有一个元素,设置自己为集合代表元 
    }
}
int find(int u){            //找点u所在集合的代表元 
    if(fa[u] != u) fa[u] = find(fa[u]);
    return fa[u]; 
}
int unin(int u, int v){    //返回0表示两个点已经在同一个联通分量 
    int fau = find(u);
    int fav = find(v);
    if(fau == fav) return 0;
    fa[fav] = fau;
    return 1; 
} 
/*
  更简短的并查集模板
  by chsobin
*/
const
int maxn = 1005; int fa[maxn]; void init(int n){ for(int i=0;i<=n;fa[i]=i++); } int find(int u){ return fa[u]==u ? fa[u] : fa[u] = find( fa[u] ); } int unin(int u, int v){ fa[find(v)] = find(u); }
/*
    并查集模板优化 秩优化+路径压缩 
    by chsobin 
*/
int const maxn = 1000000;
int fa[maxn];                //father
int rnk[maxn]; 
void init(int n){
    for(int i=0;i<=n;++i){
        fa[i] = i;            //初始时候设置每个集合只有一个元素,设置自己为集合代表元 
        rnk[i] = 0;
    }
}
int find(int u){            //找点u所在集合的代表元 
    if(fa[u] != u) fa[u] = find(fa[u]);    //优化1:路径压缩 
    return fa[u]; 
}
int unin(int u, int v){    //返回0表示两个点已经在同一个联通分量 
    int fau = find(u);
    int fav = find(v);
    if(fau == fav) return 0;
    
    if(rnk[fau] > rnk[fav])  fa[fav] = fau;    //优化2:将树矮的接到树高下 
    else {
        fa[fau] = fav;
        if(rnk[fau] == rnk[fav]) rnk[fav]++; 
    } 
    return 1; 
} 

 

题目

hdu1213

#include<iostream>
using namespace std;

const int maxn = 1005;
int fa[maxn]; //father

void init(int n){
    for(int i=0;i<=n;++i){
        fa[i] = i;            //初始时候设置每个集合只有一个元素,设置自己为集合代表元 
    }
}
int find(int u){
    if(fa[u] != u) fa[u] = find(fa[u]);
    return fa[u]; 
}
int unin(int u, int v){    //返回0表示两个点已经在同一个联通分量 
    int fau = find(u);
    int fav = find(v);
    if(fau == fav) return 0;
    fa[fav] = fau;
    return 1; 
} 
int main(){
    int t;
    cin >> t;
    while(t--){
        int n, m;
        int u, v;
        cin >> n >> m;
        init(n);
        for(int i=0;i<m;++i){
            cin >> u >> v;
            if( unin(u, v) == 1 ) n--;
        }    
        cout << n << endl;
    }
    return 0;
} 
View Code

 hdu1272

/*
不仅要判断环,还要判断是否是树  无环,点数-1==边数 
卡数据   0 0 时 
*/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

/*
    并查集模板
    by chsobin 
*/
int const maxn = 1000000;
int fa[maxn];                //father
int dot[maxn];
void init(int n){
    for(int i=0;i<=n;++i){
        fa[i] = i;            //初始时候设置每个集合只有一个元素,设置自己为集合代表元 
    }
}
int find(int u){            //找点u所在集合的代表元 
    if(fa[u] != u) fa[u] = find(fa[u]);
    return fa[u]; 
}
int unin(int u, int v){    //返回0表示两个点已经在同一个联通分量 
    int fau = find(u);
    int fav = find(v);
    if(fau == fav) return 0;
    fa[fav] = fau;
    return 1; 
} 

int main(){
    int u, v;
    int edges = 0; 
    init(100005);
    memset(dot,0,sizeof(dot));
    int dn = 0;
    bool flag = true; 
    while(cin >> u >> v && u!=-1 && v!=-1){
        if(u==0 && v==0){
            if(flag && edges == dn-1 || edges==0 && dn==0) cout << "Yes" << endl;
            else cout << "No" << endl;
            init(100005);
            memset(dot,0,sizeof(dot));
            flag = true;
            dn = 0; 
            edges = 0;
            continue;
        }
        ++edges;
        if( !dot[u] ){
            dot[u] = 1;
            dn++;
        } 
        if( !dot[v] ){
            dot[v] = 1;
            dn++;
        } 
        if(unin(u, v) == 0) flag = false; 
    }    
    return 0;
} 
ac

更多题目:http://blog.csdn.net/zy691357966/article/details/41597947

 

【参考】   

    算法导论

    顺便推荐一篇讲并查集比较有趣的博文

    https://www.cnblogs.com/TonyNeal/p/bingchaji.html

posted @ 2017-12-06 21:47  chsobin  阅读(176)  评论(0编辑  收藏  举报