RodneyX

博客园 首页 新随笔 联系 订阅 管理

并查集是一种简单而有用的数据结构,一般是用数组来实现,数组下标是元素编号,而数组内容存储的是元素所在集合的根(或者是按树形组织下的该元素前驱,而根这个概念本身也是树这一数据结构的概念)

并查集的操作非常简单包括初始化并查集,查询元素所属的集合(用根元素来标识一个集合),合并两个不相交的集合

  1. 初始化并查集:在以数组下标为元素编号的情况下,初始化需要将每个元素作为一个集合,所以可以将数组元素全部置为-1这一非法下标值,事实上数组下标未根元素的值的绝对值就是集合所含元素个数

  2. 查询元素所属的集合:数组存储的是下标元素对应的前驱,所以一直往前找到根元素即可

  3. 合并两个不相交的集合:只需将一个集合的根元素前驱设置为另一集合的根即可

针对并查集有两种优化,都是为了让查询路劲尽可能短,包括路径压缩以及按秩合并

  1. 路径压缩:在查询某元素的根时,可以将这个元素以及查询路径上的元素的前驱置为根

  2. 按秩合并:合并时将元素数量小的集合合并到大集合中

所以有如下实现

void init_disjointSet(int S[], int size)
{
    for (int i = 0; i < size; ++i)
        S[i] = -1;
}

int root_disjointSet(int S[], int size, int x)	//这个函数名一般为find
{
    if (x < 0 || x >= size)
        return -1;
    /* 被注释掉的是未优化实现
    while(S[x] >= 0)
        x = S[x];
    */
    int root_x = x;
    while (S[root_x] >= 0) // 查询所在集合
        root_x = S[root_x];
    int current;
    while (S[x] >= 0)  // 执行路径压缩
    {
        current = x;
        x = S[x]; // x指向其父节点
        S[current] = root_x;
    }
    return root_x;
}

int union_disjointSet(int S[], int size, int x, int y)
{
    if (x < 0 || x >= size || y < 0 || y >= size)
        return -1;
    int root_x = root_disjointSet(S, size, x), root_y = root_disjointSet(S, size, y);
    if (root_x == root_y)  // 是两个不同集合才能合并
        return -1;
    /* 被注释掉的是未优化实现
    S[root_x] += S[root_y];
    S[root_y] = root_x;
    */
    if (S[root_x] < S[root_y])
    { // x根下节点多
        S[root_x] += S[root_y];
        S[root_y] = root_x; // 将小树y加入大树x
    }
    else
    {
        S[root_y] += S[root_x];
        S[root_x] = root_y;
    }

    return 1;
}
posted on 2025-07-31 11:55  RodneyX  阅读(30)  评论(0)    收藏  举报