【数据结构】【王道】【树与二叉树】并查集的实现及优化(可直接运行)

总目录:https://blog.csdn.net/treesorshining/article/details/125726400

1.概述

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题,常常在使用中以森林来表示。正如其名,主要有查找与合并两个操作。

2.基本操作

2.1 初始化

初始化将所有结点的双亲设为-1。

// 并查集初始化操作
void Initial(int S[]) {
    // 初始化将所有结点的双亲设为-1
    for(int i = 0;i < SIZE;i++) {
        S[i] = -1;
    }
}

2.2 查(时间复杂度O(n))

查操作,找到x所属结合(即返回x所属根结点),时间复杂度O(n)。

// 查操作,找到x所属结合(即返回x所属根结点)
// 时间复杂度O(n)
int Find(int S[], int x) {
    // 循环寻找x的根,根的双亲为-1,循环结束则表示已找到双亲
    while(S[x] >= 0) {
        x = S[x];
    }
    return x;
}

2.3 并(时间复杂度O(1))

并操作,将两个集合合并为一个,时间复杂度O(1)。

void Union(int S[], int Root1, int Root2) {
    // Root1与Root2是相同集合则不可合并
    if(Root1 == Root2) {
        return;
    }
    // 合并即是将令一棵树称为另一棵树的子树
    // 将根Root2连接到另一根Root1下面
    S[Root2] = Root1;
}

2.4 并优化(查时间复杂度O(log2n))

Union操作优化
目的:减少Find操作的所需的时间
方法:在构建新树时,尽可能不让树长高(这样可以降低Find操作最坏时间复杂度)
具体实现方法:将根结点的值改为(-结点数,即负结点数),再根据此为依据,让小树合并到大树上
通过此优化树的高度不会超过log2n(向下取整) + 1
之后Find操作的最坏时间复杂度为O(log2n)

// Union操作优化
// 目的:减少Find操作的所需的时间
// 方法:在构建新树时,尽可能不让树长高(这样可以降低Find操作最坏时间复杂度)
// 具体实现方法:将根结点的值改为(-结点数),再根据此为依据,让小树合并到大树上
// 通过此优化树的高度不会超过log2n(向下取整) + 1
// 之后Find操作的最坏时间复杂度为O(log2n)
void UnionOptimize(int S[], int Root1, int Root2) {
    // Root1与Root2是相同集合则不可合并
    if(Root1 == Root2) {
        return;
    }
    // 比较树的结点数,将小树合并到大树
    if(S[Root2] > S[Root1]) {
        // 更新树的结点数
        S[Root1] += S[Root2];
        // 合并
        S[Root2] = Root1;
    } else {
        // 更新树的结点数
        S[Root2] += S[Root1];
        // 合并
        S[Root1] = Root2;
    }
}

2.5 查优化(时间复杂度O(a(n))

Find操作优化(压缩路径)
先找到根结点,再将查找路径上所有结点都挂到根结点下
在再次寻找时,只需要很短路径就能找到了
每次Find操作,先找根,再压缩路径,可使树的高度不超过O(a(n))
a(n)是一个增长缓慢的函数,对于常见n值,通常a(n)<=4

// Find操作优化(压缩路径)
// 先找到根结点,再将查找路径上所有结点都挂到根结点下
// 在再次寻找时,只需要很短路径就能找到了
// 每次Find操作,先找根,再压缩路径,可使树的高度不超过O(a(n))
// a(n)是一个增长缓慢的函数,对于常见n值,通常a(n)<=4
int FindOptimize(int S[], int x) {
    int root = x;
    while(S[root] >= 0) {
        // 循环找到根结点
        root = S[root];
    }
    // 压缩路径
    while(x != root) {
        // 令t指向x的父结点
        int t = S[x];
        // x直接挂到根结点下
        S[x] = root;
        x = t;
    }
    // 返回结点编号
    return root;
}

3.完整代码

#include<stdio.h>

#define SIZE 13

// 通常用数组元素下标代表元素名,用根节点下标代表子集合名,根节点双亲默认设为-1
// 集合元素数组(双亲指针数组)
int UFSets[SIZE];

// 并查集初始化操作
void Initial(int S[]) {
    // 初始化将所有结点的双亲设为-1
    for(int i = 0;i < SIZE;i++) {
        S[i] = -1;
    }
}

// 查操作,找到x所属结合(即返回x所属根结点)
// 时间复杂度O(n)
int Find(int S[], int x) {
    // 循环寻找x的根,根的双亲为-1,循环结束则表示已找到双亲
    while(S[x] >= 0) {
        x = S[x];
    }
    return x;
}

// 并操作,将两个集合合并为一个
// 时间复杂度O(1)
void Union(int S[], int Root1, int Root2) {
    // Root1与Root2是相同集合则不可合并
    if(Root1 == Root2) {
        return;
    }
    // 合并即是将令一棵树称为另一棵树的子树
    // 将根Root2连接到另一根Root1下面
    S[Root2] = Root1;
}

// Union操作优化
// 目的:减少Find操作的所需的时间
// 方法:在构建新树时,尽可能不让树长高(这样可以降低Find操作最坏时间复杂度)
// 具体实现方法:将根结点的值改为(-结点数),再根据此为依据,让小树合并到大树上
// 通过此优化树的高度不会超过log2n(向下取整) + 1
// 之后Find操作的最坏时间复杂度为O(log2n)
void UnionOptimize(int S[], int Root1, int Root2) {
    // Root1与Root2是相同集合则不可合并
    if(Root1 == Root2) {
        return;
    }
    // 比较树的结点数,将小树合并到大树
    if(S[Root2] > S[Root1]) {
        // 更新树的结点数
        S[Root1] += S[Root2];
        // 合并
        S[Root2] = Root1;
    } else {
        // 更新树的结点数
        S[Root2] += S[Root1];
        // 合并
        S[Root1] = Root2;
    }
}

// Find操作优化(压缩路径)
// 先找到根结点,再将查找路径上所有结点都挂到根结点下
// 在再次寻找时,只需要很短路径就能找到了
// 每次Find操作,先找根,再压缩路径,可使树的高度不超过O(a(n))
// a(n)是一个增长缓慢的函数,对于常见n值,通常a(n)<=4
int FindOptimize(int S[], int x) {
    int root = x;
    while(S[root] >= 0) {
        // 循环找到根结点
        root = S[root];
    }
    // 压缩路径
    while(x != root) {
        // 令t指向x的父结点
        int t = S[x];
        // x直接挂到根结点下
        S[x] = root;
        x = t;
    }
    // 返回结点编号
    return root;
}

int main() {
    int temp[13] = {-1, 0, -1, -1, 1, 1, 2, 3, 3, 3, 4, 4, 7};
    for(int i = 0;i < SIZE;i++) {
        UFSets[i] = temp[i];
    }
    printf("%d\n", Find(UFSets, 0));
    printf("%d\n", Find(UFSets, 3));
    Union(UFSets, 0, 3);
    printf("%d\n", Find(UFSets, 0));
}

4.运行结果

在这里插入图片描述

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Silver Star

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

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

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

打赏作者

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

抵扣说明:

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

余额充值