无向图中连通分量数量/无向图中是否有环 邻接矩阵 并查集 路径压缩

无向图连通分量数量:

#include <cstdio>
using namespace std;

#define MAX 1000

int father[MAX]; //指向爸爸的树的表示法

//初始化,全部置-1,(-1表示这是一个根)
void initialize(int n){
    for(int i = 0; i < n; i++) 
        father[i] = -1;
}

int find(int x){
    if(father[x] == -1)
        return x;
    else return find(father[x]);
}

void unite(int x, int y){
    int father_x = find(x);
    int father_y = find(y);
    if(father_x == father_y)  return;
    else {
        father[father_x]=y;
    }
}

int main(){
    int v = 8;
    int graph[8][8]={   //随便搞个2个连通分量的图
        0,1,1,0,0,0,0,0,
        1,0,0,0,0,0,0,0,
        1,0,0,1,0,0,0,0,
        0,0,1,0,1,0,0,0,
        0,0,0,0,1,0,0,0,
        0,0,0,0,0,0,1,1,
        0,0,0,0,0,1,0,1,
        0,0,0,0,0,1,1,0
    };

    initialize(v);

    for(int i=0;i<v;i++){
        for(int j=0;j<v;j++){
            if(graph[i][j]==1) 
                unite(i,j);
        }
    }

    int count =0;
    for(int i=0;i<v;i++){
        if(find(i) == i) count++;   //如果根节点是自己,则表明找到了一个集合
    }
    printf("%d\n", count);
}

是否有环?

(两个点已经在一个集合里了结果又冒出来一条边,就有环)

int main(){
    int v = 8;
    int graph[8][8]={   //无环的
        0,1,1,0,0,0,0,0,
        1,0,0,0,0,0,0,0,
        1,0,0,1,0,0,0,0,
        0,0,1,0,1,0,0,0,
        0,0,0,0,1,0,0,0,
        0,0,0,0,0,0,0,1,
        0,0,0,0,0,0,0,1,
        0,0,0,0,0,1,1,0
    };

    initialize(v);

    //无向图只遍历上三角!!
    for(int i=0;i<v;i++){
        for(int j=i+1;j<v;j++){
            if(graph[i][j]==1){
                int father_i = find(i);
                int father_j = find(j);
                if( father_i != father_j){
                    unite(i,j);
                }
                else {
                    printf("有环\n");
                    return 1;
                };
            }
        }
    }
    printf("无环\n");
    return 0;
}

优化:压缩路径

  • 查的时候,将叶子变成儿子
  • 并的时候,元素少的的并到元素多的的
  • 如何记录元素数量?根结点原本用-1表示,可以改成-n。也就是用根的绝对值表示元素数量。
int find(int x){
    int father_x = x;
    while(father[father_x] >=0 ){
        father_x = father[x];
    }
    father[x]=father[father_x]; //路径压缩,把元素直接挂在根结点下面那层
    return father_x;
}

void unite(int x, int y){
    int father_x = find(x);
    int father_y = find(y);
    if(father_x == father_y)  return;
    else {
        if(father_x<father_y){        //x的那个集合元素更多,将元素较少的y的集合并到x的集合
            father[father_x]+=father[father_y];	//修改元素数量
            father[father_y]=father_x;
        }else{
            father[father_y]+=father[father_x];
            father[father_x]=father_y;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值