C++并查集的原理与使用方法
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一、并查集的概念</li><li>二、并查集的实现</li><li>三、算法题中的应用</li><li>总结</li></ul></div><p class="maodian"></p><h2>一、并查集的概念</h2><p><strong>在一些场景中,需要将n个不同元素划分为一些不相交的集合。开始时,每个元素各成一个元素,然后按一定的规律将属于同一组的元素合并。这个过程中需要反复用到查询一个元素是否属于某个集合的算法。适合用于这种场景的数据结构是并查集(Union-Find Set)!</strong></p>
<p>并查集的底层结构本质上是一片森林(多棵树的集合)</p>
<p>比如,我现在有九个数据元素,给他们编号0~8:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/20251221144946621.png" /></p>
<p>按照某种需求,这些数据被分组合并为:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/20251221144946622.png" /></p>
<p>按照其他需求,这些树可以继续合并下去…</p>
<p><strong>而这个森林,可以用一个数组记录下来元素的关系!</strong><br /><strong>我们可以规定并查集用数组下标代表每个元素,数组内容代表元素之间的关系:</strong></p>
<ul><li><strong>数组下标代表元素编号</strong></li><li><strong>如果数组内容为负整数,代表这个下标是根,绝对值表示它这棵树的元素个数</strong></li><li><strong>如果数组内容为非负整数,代表这个下标不是根,数组内容是它的父亲在数组中的下标</strong></li></ul>
<p>比如,上面的森林例子,用并查集数组表示,就是:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122114453872.png" /></p>
<p><strong>如果元素的数据类型不能直接作为数组下标,只要在实现中用std::map之类的结构,建立元素到下标的映射关系,就能解决了!</strong></p>
<p>通过并查集的特点,可以看出并查集一般能解决:</p>
<ul><li><strong>查找元素属于哪个集合:沿着数组一直找到元素为负数,就是根</strong></li><li><strong>查看两个元素是否属于一个集合:看看这两个元素的根是否相同</strong></li><li><strong>将两个集合归并为一个集合:假如要将下标a的树合并到下标b的树中,arr += arr,arr = b即可,即令1成为0的一个孩子</strong></li><li><strong>统计集合的个数:统计数组中元素为负数的个数</strong></li></ul>
<p class="maodian"></p><h2>二、并查集的实现</h2>
<div class="jb51code"><pre class="brush:cpp;">#pragma once
#include<vector>
#include<iostream>
using namespace std;
class UnionFindSet
{
public:
UnionFindSet(int size)
:_set(size, -1) // 初始时每个数据各是一棵树,元素均为-1
{ }
// 查找一个数据属于哪个集合,找根元素的下标
int FindRoot(int i)
{
while (_set >= 0)
{
i = _set;
}
return i;
}
// 合并两个数据所在的集合
void Union(int i1, int i2)
{
// 找这两个数据的根下标
int root1 = FindRoot(i1);
int root2 = FindRoot(i2);
if (root1 != root2)
{
_set += _set;
_set = root1;
}
// 如果root1 == root2,说明这两个数据本就在一个集合,不用合并
}
// 判断两个数据是否在同一个集合
bool IsSameSet(int i1, int i2)
{
return FindRoot(i1) == FindRoot(i2);
}
// 统计集合个数
int SetCount()
{
int ret = 0;
for (int n : _set)
{
if (n < 0)
ret++;
}
return ret;
}
private:
vector<int> _set;
};
</pre></div>
<p>测试:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122114453929.png" /></p>
<p class="maodian"></p><h2>三、算法题中的应用</h2>
<p>并查集的特点在某些算法题中很有用:</p>
<ul><li>省份数量
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122114453954.png" /></p></li></ul>
<div class="jb51code"><pre class="brush:cpp;">class Solution {
public:
// 并查集, 统计集合数量
int findCircleNum(vector<vector<int>>& isConnected) {
vector<int> ufs(isConnected.size(), -1);
auto findRoot = [&ufs](int i)
{
while(ufs >= 0)
{
i = ufs;
}
return i;
};
auto Union = [&ufs, &findRoot](int i1, int i2)
{
int root1 = findRoot(i1);
int root2 = findRoot(i2);
if(root1 != root2)
{
ufs += ufs;
ufs = root1;
}
};
auto SetCount = [&ufs]()
{
int ret = 0;
for(int n : ufs)
{
if(n < 0)
ret++;
}
return ret;
};
for(int i = 0; i < isConnected.size(); i++)
{
for(int j = 0; j < isConnected.size(); j++)
{
if(isConnected == 1)
{
Union(i, j);
}
}
}
return SetCount();
}
};
</pre></div>
<ul><li>等式方程的可满足性
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202512/2025122114453980.png" /></p></li></ul>
<div class="jb51code"><pre class="brush:cpp;">class Solution {
public:
// 并查集,数组大小26
// 遍历一次把所有==的两个字母放到一个集合,再遍历一次看!=的两个字符是否都在集合中出现过,出现过则false
bool equationsPossible(vector<string>& equations) {
vector<int> ufs(26, -1);
auto findRoot = [&ufs](int i)
{
while(ufs >= 0)
{
i = ufs;
}
return i;
};
auto Union = [&ufs, &findRoot](int i1, int i2)
{
int root1 = findRoot(i1);
int root2 = findRoot(i2);
if(root1 != root2)
{
ufs += ufs;
ufs = root1;
}
};
for(string& s : equations)
{
if(s == '=')
{
Union(s-'a', s-'a');
}
}
for(string& s : equations)
{
if(s == '!')
{
int root1 = findRoot(s-'a');
int root2 = findRoot(s-'a');
if(root1 == root2)
{
return false;
}
}
}
return true;
}
};
</pre></div>
<p class="maodian"></p><h2>总结</h2>
<p>到此这篇关于C++并查集的原理与使用方法的文章就介绍到这了,更多相关C++并查集原理与使用内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>C/C++并查集的查询与合并实现原理</li><li>C++数据结构之并查集详解</li><li>C++实现并查集</li><li>C++并查集算法简单详解</li><li>C++并查集常用操作</li><li>C++高级数据结构之并查集</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]