【算法模板】 并查集

237 阅读2分钟

# 并查集

## 1 概念介绍

在计算机科学中,并查集是一种树形结构,用来处理不交集(Disjoint Sets)得合并和查询问题[1]。并查集主要是有两个操作,依次是:

- Find : 查找

- Union :合并

其中Find是判断给定元素属于哪一类别,常用一组类别中某个元素作为Root元素,表征这一类别,那么Find操作其实就是这个元素对应的Root值为多少。两个元素的Root值相同,则该两个元素为同一类别。

在构建并查集时,会遇到将两个类别合并为一个类别,即为Union。按上述对于Find操作的描述,即将原两个类别中所有元素对应的Root值改成同一值。

## 2 代码模板

### 2.1 朴素并查集

不考虑效率,从定义上实现并查集Find和Union两个操作。核心点就是用一个p数组维护该元素的父节点元素值或Root值(`路径压缩`),当一个元素的p值为其本身时,则该节点为Root节点。

其中p值为元素对应的Root时并查集效率`更高`,以下模板给出了这种情况下的算法实现。

```C++

void MakeSet(int size)

{

// 初始化并查集,将每个元素的p值设置为其本身

// 假设节点值是1~size(含size)

for (int i = 1; i <= size; i++) {

p[i] = i;

}

}

int Find(int n)

{

// 判断元素n的类别,即查找元素n的Root元素值

if (p[n] != n) {

p[n] = Find(p[n]); // 非Root节点,继续向上查找,并赋值

}

return p[n];

}

void Union(int n, int m)

{

// 将元素n和元素m所在类别合并

// 只需将n和m中的一个元素的Root指向另一个元素的Root

int rootA = Find(n);

int rootB = Find(m);

p[rootA] = rootB; // n的Root指向m的Root

}

```

### 2.2 维护元素集Size的并查集

与朴素并查集算法实现不用的是,这里维护了一个size数组保存每个类别中的当前的元素数量,预设只有Root节点的size数组值才有效。

```C++

void MakeSet(int size)

{

// 初始化并查集,将每个元素的p值设置为其本身

// 假设节点值是1~size(含size)

for (int i = 1; i <= size; i++) {

p[i] = i;

// ! 增加size数组

size[i] = 1;

}

}

int Find(int n)

{

// 判断元素n的类别,即查找元素n的Root元素值

// ! Find操作是不需更新size数组

if (p[n] != n) {

p[n] = Find(p[n]); // 非Root节点,继续向上查找,并赋值

}

return p[n];

}

void Union(int n, int m)

{

// 将元素n和元素m所在类别合并

// 只需将n和m中的一个元素的Root指向另一个元素的Root

int rootA = Find(n);

int rootB = Find(m);

p[rootA] = rootB; // n的Root指向m的Root

// ! rootB为新的Root, 只需更新rootB的size值

size[rootB] += size[rootA];

}

```

## 相关leetcode例题

[LeetCode并查集专题](leetcode-cn.com/tag/union-f…)

[1] [并查集-维基百科](zh.wikipedia.org/wiki/%E5%B9…)