数据结构之并查集

并查集是一种数据结构,用于管理元素集合,通过森林表示各集合。基本操作包括初始化、查询和合并。查询找到元素的根节点,合并则将两集合根节点相连。路径压缩通过递归查询直接连接节点到根节点,减少查询次数。启发式合并考虑节点数量,避免合并导致结构退化,提高效率。
摘要由CSDN通过智能技术生成

数据结构之并查集

一、定义

并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。

在这里插入图片描述

​ 注:每棵树表示一个集合,节点表示集合中的元素

二、基本实现

并查集的最基本的三种操作,一个是初始化,一个是查询,一个是合并。

  • 查询:找到元素所处集合的根节点。在这里插入图片描述

  • 合并:将两个元素所处的集合进行合并,其实质是将其中一个元素所处集合的根节点的父节点设为另一个元素所处集合的根节点。在这里插入图片描述

    下面是一个简单并查集的实现:

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
class Uls{
/* pa[x] = a 表示x的父节点为a */
public:
//  初始化集合:把每个单独节点的父亲设为本身(所处集合只有本身)  pa[x] = x 代表x的父亲就是x
    explicit Uls(size_t size);

//  查询操作:利用递归查询出x所处集合的根节点 (或者是集合的代表元素)
    size_t find(size_t x);
    
//  合并两个元素所处的集合
    void Union(size_t x ,size_t y);

private:
    
    vector<size_t> pa;

};

Uls::Uls(size_t size):pa(size){
//    从0开递增初始化pa
    iota(pa.begin(),pa.end(),0);
}

size_t Uls::find(size_t x) {
//	  找到所处集合的根节点
    return pa[x] == x ? x : find(pa[x]);
}

void Uls::Union(size_t x, size_t y) {
//    把x所在集合的根节点的父亲设为y所在集合的根节点 这样两个元素所在的集合就合并了
    pa[find(x)] = find(y);
}

三、改进

1、路径压缩

上述查询的方式意味着每查询一次都要进行一次递归,实际上位于同一集合的元素只需要查询一次,因为它们所处同一个集合,拥有同一个根节点。所以该操作能采取路径压缩的方法:即查询过程中经过的节点都属于该集合,我们可以将其直接连接到根节点。

在这里插入图片描述

路径压缩后查询函数实现如下:

size_t Uls::find(size_t x) {
//	重点是pa[x] = find(pa[x]) 递归每经过一个节点就将该节点的父节点直接设为根节点 以便后续的查询
    return pa[x] == x ? x : pa[x] = find(pa[x]);
}

2.启发式合并

合并时,选择哪棵树的根节点作为新树的根节点会影响未来操作的复杂度。我们可以将节点较少或深度较小的树连到另一棵,以免发生退化。

那节点数合并的参考实现:

struct Uls {
  vector<size_t> pa, size;

  explicit Uls(size_t size_) : pa(size_), size(size_, 1) {
    iota(pa.begin(), pa.end(), 0);
  }

  void unite(size_t x, size_t y) {
    size_t x_root = find(x);
    size_t y_root = find(y);
    if (x == y) return;
    
    if (size[x_root] < size[y_root]) swap(x_root, y_root);
    pa[y_root] = x_root;
    size[x_root] += size[y_root];
  }
};

参考:并查集 - OI Wiki (oi-wiki.org)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值