并查集

生活中有很多连通性问题。解决方案——并查集。

1. quickFind算法

并查集的第一个算法:Quick-Find

算法思想:

  1. 基于染色思想,一开始每个人都是自己一个颜色
  2. 连接两个点时,把两个点设置成一个颜色。
  3. 如果两个点颜色一样,说明是一伙的,否则不是

算法优劣:
联通判断:O(1)
合并操作:O(n)

优化思考:

  1. quick-find算法的联通判断很快,合并操作很慢
  2. 本质上就是知道谁和谁的颜色一样
  3. 联通的点都指向一个节点
  4. 合并操作是让一棵树中的所有点指向另一棵数的根是否有优化空间?

编码设计:(C语言)

#include<stdio.h>
#include<stdlib.h>

// 并查集的基础结构——color数组+size大小
typedef struct UnionSet {
    int *color; // 存储每个人的颜色
    int size; // 代表有多少个人
}UnionSet;

// 并查集的初始化
UnionSet *initSet(int n) {
    UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
    u->color = (int *)malloc(sizeof(int) * n);
    u->size = n;
    for(int i=0;i<u->size;i++) u->color[i] = i; // 初始化每个人都是自己一个颜色
    return u;
}

// 内存的释放
void freeSet(UnionSet *u) {
    if(!u) return;
    free(u->color); // 释放顺序:先释放里面的零部件,后释放整个并查集
    free(u);
    return ;
}

// 通过下标寻找当前节点在并查集中的所属颜色。
int find(UnionSet *u, int idx){
    return u->color[idx];
}

// 完成合并操作
int merge(UnionSet *u,int a,int b) {
    if(find(u,a)==find(u,b)) return 0;
    int acolor = find(u,a); // 获取a的颜色
    int i;
    for(i=0;i<u->size;i++){ // 把所有是a的颜色的人改为b的颜色——将a合并到b中
        if(find(u,i) == acolor){
            u->color[i] = u->color[b];
        }
    }
    return 1;
}

int main(){
    int n,m; //  n为节点的个数, m代表边的个数。
    scanf("%d%d", &n,&m);
    UnionSet *u = initSet(n + 1);
    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        switch(a){
            case 1: // 如果是1就合并b,c
                merge(u,b,c);
                break;
            case 2: // 如果是2,就输出b和c的关系
                printf("%s\n", find(u,b)==find(u,c)?"Yes":"No");
                break;
        }
    }
    freeSet(u);
    return 0;
}

C++版:

#include<iostream>
#include<cstdio>
using namespace std;
class UnionSet {
public:
    UnionSet(int n) : color(new int[n]), size(n) {
        for (int i = 0; i < n; i++) color[i] = i;
    }
    int find(int ind) {
        return color[ind];
    }
    int merge(int a,int b) {
        int fa = find(a), fb = find(b);
        if(fa == fb) return 0;
        for(int i=0;i<size;i++){
            if(find(i) == fa) color[i] = fb;
        }
        return 1;
    }
    ~UnionSet() {
        delete[] color;
    }
    int *color;
    int size;
};

int main() {
    int n,m;
    cin >> n >> m;
    UnionSet u(n + 1);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        switch(a) {
            case 1: {
                u.merge(b, c);
                break;
            }
            case 2: {
                // 判断是否联通
                printf("%s\n", u.find(b) == u.find(c)?"Yes":"No");
                break;
            }
        }
    }
    return 0;
}

样例输入

6 5
1 1 2
2 1 3
1 2 4
1 4 3
2 1 3

样例输出

No
Yes

2. quickUnion算法

  1. 一开始每个人都是自己的一个队友,相当于没有队友。
  2. 连接你和你的队友时,让他没有队友的队友,作为你没有队友的队友,的队友
  3. 如果两个人,没有队友的队友,是一个人,说明他们是队友。
class UnionSet {
public:
    UnionSet(int n) : father(new int[n]), size(n) {
        for(int i=0;i<n;i++){
            father[i]=i;
        }
    }
    int find(int ind) {
        if(father[ind] == ind) return ind;
        return find(father[ind]);
    }
    void merge(int a,int b) {
        int fa = find(a), fb = find(b);
        if(fa ==fb) return ;
        father[fa] = fb;
        return ;
    }
    int *father;
    int size;
    ~UnionSet(){
        delete[] father;
    }
};

3.weighted quickUnion算法原理

如果一个帮派的人数大于另一个帮派的人数。那么当这两个帮派合并的时候,人数少的一方自然要认人数多的一方。weighted quickUnion算法也是如此。

class UnionSet {
public:
    UnionSet(int n) : father(new int[n]), len(new int[n]), size(n) {
        for(int i=0;i<size;i++) {
            father[i]=i;
            len[i]=i;
        }
    }
    int find(int ind) {
        if(father[ind] == ind) return ind;
        return find(father[ind]);
    }
    int merge(int a,int b) {
        int fa=find(a), fb=find(b);
        if(fa==fb) return 0;
        if(len[fa]>len[fb]){
            father[fb]=fa;
            len[fa]+=len[fb];
        }else{
            father[fa]=fb;
            len[fb]+=len[fa];
        }
        return 1;
    }
    ~UnionSet() {
        delete[] len;
        delete[] father;
    }
    int *father, *len, size; // 增加一个变量len
};

4. 路径压缩代码:

在寻找父亲find()的路上,如果找到了最终的父亲,那么之前一路以来的所有孩子都可以直接认它为父节点。这样当再次查找的时候就可以简单很多。

class UnionSet {
public:
    UnionSet(int n) : father(new int[n]), size(n) {
        for(int i=0;i<n;i++){
            father[i]=i;
        }
    }
    // 路径压缩核心代码:
    int find(int ind) {
        return father[ind] = father[ind]==ind?ind:find(father[ind]);
    }
    void merge(int a,int b) {
        int fa = find(a), fb = find(b);
        if(fa ==fb) return ;
        father[fa] = fb;
        return ;
    }
    int *father;
    int size;
    ~UnionSet(){
        delete[] father;
    }
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容