【数据结构】并查集

并查集

并查集(Disjoint-Set)是一种数据结构,用于管理一组元素的分组情况,并提供两种操作:合并(Union)和查找(Find)。并查集主要用于解决连通性问题,例如判断元素是否在同一集合中,并在需要时合并两个集合。常见的应用场景包括网络连接问题、等价关系问题和最小生成树算法等。

并查集的基本原理

并查集的数据结构主要由两部分组成:

  • 父节点数组:表示每个元素的父节点。初始时,每个元素的父节点指向自己,表示每个元素都是一个独立的集合。
  • 秩数组:表示每个集合的秩(或高度)。秩用于优化合并操作,保持集合的平衡性。

并查集提供两种主要操作:

  • 查找(Find):查找元素所属的集合,并通过路径压缩优化,使查找操作更快。
  • 合并(Union):合并两个集合,并通过秩优化,保持树的平衡。

并查集的优点

  • 高效的操作:通过路径压缩和秩优化,使查找和合并操作的时间复杂度接近于常数。
  • 解决连通性问题:并查集适用于解决连通性问题,如网络连接和等价关系问题。

并查集的缺点

  • 内存开销:需要额外的内存来存储父节点和秩数组。
  • 只能用于特定问题:并查集主要用于连通性问题,对于其他类型的问题可能不适用。

C语言中的并查集示例

下面是一个使用C语言实现的并查集示例,展示了基本的查找和合并操作。

首先,定义并查集的数据结构和相关操作:

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

// 并查集结构
typedef struct {
    int *parent;
    int *rank;
    int size;
} DisjointSet;

// 初始化并查集
DisjointSet *initDisjointSet(int size) {
    DisjointSet *ds = (DisjointSet *)malloc(sizeof(DisjointSet));
    ds->size = size;
    ds->parent = (int *)malloc(size * sizeof(int));
    ds->rank = (int *)malloc(size * sizeof(int));

    // 初始化父节点数组和秩数组
    for (int i = 0; i < size; i++) {
        ds->parent[i] = i; // 每个元素的父节点指向自己
        ds->rank[i] = 0;   // 初始秩为0
    }
    return ds;
}

// 释放并查集
void freeDisjointSet(DisjointSet *ds) {
    free(ds->parent);
    free(ds->rank);
    free(ds);
}

// 查找操作(路径压缩)
int find(DisjointSet *ds, int x) {
    if (ds->parent[x] != x) {
        // 路径压缩
        ds->parent[x] = find(ds, ds->parent[x]);
    }
    return ds->parent[x];
}

// 合并操作(秩优化)
void unionSets(DisjointSet *ds, int x, int y) {
    int rootX = find(ds, x);
    int rootY = find(ds, y);

    if (rootX != rootY) {
        // 根据秩合并
        if (ds->rank[rootX] < ds->rank[rootY]) {
            ds->parent[rootX] = rootY;
        } else if (ds->rank[rootY] < ds->rank[rootX]) {
            ds->parent[rootY] = rootX;
        } else {
            ds->parent[rootY] = rootX;
            ds->rank[rootX]++;
        }
    }
}

在上面的代码中,定义了并查集的数据结构 DisjointSet,包含一个父节点数组 parent 和秩数组 rank。同时,定义了查找和合并操作。

  • 查找操作:通过递归查找元素的根节点,并使用路径压缩优化操作,使元素的父节点直接指向根节点,从而加快查找操作的速度。
  • 合并操作:通过秩优化合并两个集合,根据秩决定哪个集合的根节点作为合并后的根节点,以保持集合的平衡。

接下来,示例代码展示了如何使用并查集:

int main() {
    // 初始化并查集
    int size = 10; // 元素数量
    DisjointSet *ds = initDisjointSet(size);

    // 合并元素
    unionSets(ds, 1, 2);
    unionSets(ds, 3, 4);
    unionSets(ds, 5, 6);
    unionSets(ds, 7, 8);
    unionSets(ds, 1, 3);
    unionSets(ds, 5, 7);

    // 检查元素是否在同一集合
    printf("元素1和2是否在同一集合:%s\n", find(ds, 1) == find(ds, 2) ? "是" : "否");
    printf("元素3和4是否在同一集合:%s\n", find(ds, 3) == find(ds, 4) ? "是" : "否");
    printf("元素5和6是否在同一集合:%s\n", find(ds, 5) == find(ds, 6) ? "是" : "否");
    printf("元素7和8是否在同一集合:%s\n", find(ds, 7) == find(ds, 8) ? "是" : "否");
    printf("元素1和3是否在同一集合:%s\n", find(ds, 1) == find(ds, 3) ? "是" : "否");
    printf("元素5和7是否在同一集合:%s\n", find(ds, 5) == find(ds, 7) ? "是" : "否");

    // 释放并查集
    freeDisjointSet(ds);

    return 0;
}

在上面的代码中,我们首先初始化一个并查集,然后通过 unionSets() 函数合并元素 (1, 2)(3, 4)(5, 6)(7, 8)(1, 3)(5, 7)。接着,通过调用 find() 函数检查这些元素是否在同一集合中。

总结

并查集是一种高效的数据结构,适用于解决连通性问题。通过路径压缩和秩优化,使查找和合并操作的时间复杂度接近于常数。并查集在许多图论和算法问题中具有广泛的应用,如网络连接问题和最小生成树算法等。

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

游向大厂的咸鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值