5.5并查集的实现和基本应用

代码如下

不太理解的地方可以先看注释,注释看不懂可以留言或者私信我,如果有写错的地方欢迎大家指出谢谢!!

#include <stdio.h>
#define MAX 5

//采用邻接矩阵存储的无向图(此图有2连通图,并且存在环)
int g[MAX][MAX]={0,3,1,0,0,
                3,0,4,0,0,
                1,4,0,0,0,
                0,0,0,0,2,
                0,0,0,2,0
};

/**
 * 并查集查找根节点
 * @param A 并查集
 * @param x 待查找根节点的节点
 * @return
 */
int find(int A[],int x){
    int root = x;
    while (A[root]>=0) root  = A[root];//依次向上找到根节点
    //把寻找路径上的节点全部设置为根节点,方便下次寻找
    while (x!=root){
        int t = A[x];
        A[x] = root;
        x = t;
    }
    return root;
}

/**
 * 并查集的合并操作,根节点的绝对值表示当前根节点下一共有多少节点,每次合并小树往大树合并
 * @param A 并查集
 * @param Root1 待合并的根1
 * @param Root2 待合并的根2
 * @return
 */
bool Union(int A[],int Root1,int Root2){
    if(Root1==Root2) return false;//Root1和Root2相等则不需要合并
    //判断谁是大树谁是小树
    if(A[Root2]>A[Root1]) {
        A[Root1] += A[Root2];//小树的根节点数加到大树上
        A[Root2] = Root1;//小树的根节点设置为大树的根节点
    } else{
        A[Root2] += A[Root1];
        A[Root1] = Root2;
    }
}

/**
 * 初始化并查集,根节点全部初始为-1
 * @param A 并查集
 */
void init(int A[]){
    for (int i = 0; i < MAX; ++i) {
        A[i] = -1;
    }
}

/**
 * 并查集的应用,查找连通图的数量
 * @param G 采用邻接矩阵存储的无向图
 * @return 返回连通图的数量
 */
int ComponentCount(int G[MAX][MAX]){
    int S[MAX];
    int count;
    init(S);
    //把连通的节点加入同一个集合中,不连通的节点则不处于同一个集合中
    for (int i = 0; i < MAX; ++i)
        //因为是无向图,所以只用遍历上三角区域
        for (int j = i+1; j < MAX; ++j)
            //说明i到j有边
            if(G[i][j]>0){
                int iRoot = find(S,i);//找到i的根节点
                int jRoot = find(S,j);//找到j的根节点
                if(iRoot != jRoot)//判断是否处于同一个并查集中
                    Union(S,iRoot,jRoot);//不在同一个并查集则合并
            }
    //遍历一次并查集,小于0的节点说明为根节点,有几个根节点说明有几个集合,即连通图的数量
    for (int i = 0; i < MAX; ++i) {
        if(S[i]<0) count++;
    }
    return count;
}

/**
 * 判断图中是否存在环
 * @param G 采用邻接矩阵存储的无向图
 * @return 有环返回true,没有环返回false
 */
bool hasAcyclic(int G[MAX][MAX]){
    int S[MAX];
    init(S);
    //把连通的节点加入同一个集合中,不连通的节点则不处于同一个集合中
    for (int i = 0; i < MAX; ++i)
        //因为是无向图,所以只用遍历上三角区域
        for (int j = i+1; j < MAX; ++j)
            if(G[i][j]>0){
                int iRoot = find(S,i);//找到i的根节点
                int jRoot = find(S,j);//找到j的根节点
                if(iRoot != jRoot)//判断是否处于同一个并查集中
                    Union(S,iRoot,jRoot);//不在同一个并查集则合并
                else return true;//在同一个并查集则说明之前已经加入过,属于同一个集合,此时又出现一个联通边,说明存在环,直接返回true
            }
    return false;//遍历完成没有发现环,返回false
}

int main() {

    printf("%d\n", ComponentCount(g));

    if(hasAcyclic(g))
        printf("hasAcyclic\n");
    else printf("noAcyclic\n");

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天地神仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值