news 2026/4/27 14:20:05

C++并查集的原理与使用方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++并查集的原理与使用方法

一、并查集的概念

在一些场景中,需要将n个不同元素划分为一些不相交的集合。开始时,每个元素各成一个元素,然后按一定的规律将属于同一组的元素合并。这个过程中需要反复用到查询一个元素是否属于某个集合的算法。适合用于这种场景的数据结构是并查集(Union-Find Set)!

并查集的底层结构本质上是一片森林(多棵树的集合)

比如,我现在有九个数据元素,给他们编号0~8:

按照某种需求,这些数据被分组合并为:

按照其他需求,这些树可以继续合并下去…

而这个森林,可以用一个数组记录下来元素的关系!
我们可以规定并查集用数组下标代表每个元素,数组内容代表元素之间的关系:

  • 数组下标代表元素编号
  • 如果数组内容为负整数,代表这个下标是根,绝对值表示它这棵树的元素个数
  • 如果数组内容为非负整数,代表这个下标不是根,数组内容是它的父亲在数组中的下标

比如,上面的森林例子,用并查集数组表示,就是:

如果元素的数据类型不能直接作为数组下标,只要在实现中用std::map之类的结构,建立元素到下标的映射关系,就能解决了!

通过并查集的特点,可以看出并查集一般能解决:

  • 查找元素属于哪个集合:沿着数组一直找到元素为负数,就是根
  • 查看两个元素是否属于一个集合:看看这两个元素的根是否相同
  • 将两个集合归并为一个集合:假如要将下标a的树合并到下标b的树中,arr[b] += arr[a],arr[a] = b即可,即令1成为0的一个孩子
  • 统计集合的个数:统计数组中元素为负数的个数

二、并查集的实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

#pragma once

#include<vector>

#include<iostream>

usingnamespacestd;

classUnionFindSet

{

public:

UnionFindSet(intsize)

:_set(size, -1)// 初始时每个数据各是一棵树,元素均为-1

{ }

// 查找一个数据属于哪个集合,找根元素的下标

intFindRoot(inti)

{

while(_set[i] >= 0)

{

i = _set[i];

}

returni;

}

// 合并两个数据所在的集合

voidUnion(inti1,inti2)

{

// 找这两个数据的根下标

introot1 = FindRoot(i1);

introot2 = FindRoot(i2);

if(root1 != root2)

{

_set[root1] += _set[root2];

_set[root2] = root1;

}

// 如果root1 == root2,说明这两个数据本就在一个集合,不用合并

}

// 判断两个数据是否在同一个集合

boolIsSameSet(inti1,inti2)

{

returnFindRoot(i1) == FindRoot(i2);

}

// 统计集合个数

intSetCount()

{

intret = 0;

for(intn : _set)

{

if(n < 0)

ret++;

}

returnret;

}

private:

vector<int> _set;

};

测试:

三、算法题中的应用

并查集的特点在某些算法题中很有用:

  • 省份数量

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

classSolution {

public:

// 并查集, 统计集合数量

intfindCircleNum(vector<vector<int>>& isConnected) {

vector<int> ufs(isConnected.size(), -1);

auto findRoot = [&ufs](inti)

{

while(ufs[i] >= 0)

{

i = ufs[i];

}

returni;

};

auto Union = [&ufs, &findRoot](inti1,inti2)

{

introot1 = findRoot(i1);

introot2 = findRoot(i2);

if(root1 != root2)

{

ufs[root1] += ufs[root2];

ufs[root2] = root1;

}

};

auto SetCount = [&ufs]()

{

intret = 0;

for(intn : ufs)

{

if(n < 0)

ret++;

}

returnret;

};

for(inti = 0; i < isConnected.size(); i++)

{

for(intj = 0; j < isConnected[i].size(); j++)

{

if(isConnected[i][j] == 1)

{

Union(i, j);

}

}

}

returnSetCount();

}

};

  • 等式方程的可满足性

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

classSolution {

public:

// 并查集,数组大小26

// 遍历一次把所有==的两个字母放到一个集合,再遍历一次看!=的两个字符是否都在集合中出现过,出现过则false

boolequationsPossible(vector<string>& equations) {

vector<int> ufs(26, -1);

auto findRoot = [&ufs](inti)

{

while(ufs[i] >= 0)

{

i = ufs[i];

}

returni;

};

auto Union = [&ufs, &findRoot](inti1,inti2)

{

introot1 = findRoot(i1);

introot2 = findRoot(i2);

if(root1 != root2)

{

ufs[root1] += ufs[root2];

ufs[root2] = root1;

}

};

for(string& s : equations)

{

if(s[1] =='=')

{

Union(s[0]-'a', s[3]-'a');

}

}

for(string& s : equations)

{

if(s[1] =='!')

{

introot1 = findRoot(s[0]-'a');

introot2 = findRoot(s[3]-'a');

if(root1 == root2)

{

returnfalse;

}

}

}

returntrue;

}

};

总结

到此这篇关于C++并查集的原理与使用方法的文章就介绍到这了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 14:18:22

终极指南:如何用AI图像超分辨率让模糊图像瞬间清晰

终极指南&#xff1a;如何用AI图像超分辨率让模糊图像瞬间清晰 【免费下载链接】Real-ESRGAN-ncnn-vulkan NCNN implementation of Real-ESRGAN. Real-ESRGAN aims at developing Practical Algorithms for General Image Restoration. 项目地址: https://gitcode.com/gh_mir…

作者头像 李华
网站建设 2026/4/27 14:15:20

制作实时数字人系统门槛大降,千元级硬件即可快速部署,支持高并发本地无限免费克隆数字人

产品介绍 原始宣传文档 我们的Pioneerx Human实时数字人系统整体响应最快可到0.5-0.7毫秒&#xff0c;以上参数使用2080ti 22gb显卡做为参考。之所以有如此之快的响应速度&#xff0c;不仅得益于我们开发团队长期的优化底层算法和长期技术积累&#xff0c;在实时数字人领域不断…

作者头像 李华
网站建设 2026/4/27 14:08:24

Go 模块依赖管理策略

Go模块依赖管理策略解析 随着Go语言的快速发展&#xff0c;高效的依赖管理成为开发者关注的焦点。Go模块&#xff08;Go Modules&#xff09;自1.11版本引入后&#xff0c;逐渐取代了传统的GOPATH模式&#xff0c;成为官方推荐的依赖管理方案。它不仅解决了版本控制问题&#…

作者头像 李华
网站建设 2026/4/27 14:02:59

04-10-10 《学会提问》博客系列

04-10-10 《学会提问》博客系列 系列说明 本系列基于 Neil Browne 和 Stuart Keeley 的经典著作《Asking the Right Questions》(学会提问)&#xff0c;将批判性思维的核心方法转化为9篇实用博客文章。作为技术人&#xff0c;我们每天都在接收大量信息、做技术决策、评估方案…

作者头像 李华