当前位置: 首页 > article >正文

数据结构-并查集

并查集原理   

  在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合开始时,每个元素自成一个 单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询一
个元素归属于那个集合的运算。适合于描述这类问题的抽象数据类型称为并查集(union-find
set)
  
  

  一般可以用数组来表示并查集,数据的下标就是每个数据的编号,对应的值如果是负数,那么就代表它自成一个集合,也就是一个根结点。如图,初始值一般都是负数,也就是每个元素都自成一个集合,如果一个元素是根,那么负数的绝对值则代表这个树有多少结点;如果不是负数,那么则说明这个元素不是根,且对应的值存的是父亲结点的下标。

  实际应用中也可以通过加入哈希表来设置更多的映射关系,这样的话可以将数组的下标作为key值,然后可以存人名这些。

  所以并查集其实也是多棵树组成的森林。

用并查集表示就是

 

 元素之间可以合并,并且可以制定合并规则。集合与集合之间也可以合并。

 综上,我们可以知道并查集可以解决以下问题:

1.查找某个元素属于哪个集合。沿着数组表示的树形关系向上可以一直找到根。

2.查看两个元素是否属于同一个集合。可以看找到的根是否相同,以此来判断是否属于同一个集合。

3.可以将两个集合归并成一个集合。

4.求集合的个数。其实也就是根的个数,可以遍历数组,记录下标为负数的元素即是集合的个数。

并查集简单实现

class UnionFindSet
{
public:
	UnionFindSet(int size)
		:_set(size, -1)
	{}
	~UnionFindSet()
	{}
	size_t FindRoot(int x)
	{
		while (_set[x] >= 0)
			x = _set[x];  // 向上找到根
		return x;
	}

	void Union(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);

		if (root1 != root2)  // 如果相等说明在一个集合内,没必要合并
		{
			_set[root1] += _set[root2];
			_set[root2] = root1;    // 这里默认合并到root1
		}
	}

	int SetCount()  // 找根结点个数
	{
		size_t count = 0;
		for (size_t i = 0; i < _set.size(); ++i)
		{
			if (_set[i] < 0)
				count++;
		}
		return count;
	}
private:
	std::vector<int> _set;
};

void TestUFS()
{
	UnionFindSet u(10);

	u.Union(0, 6);
	u.Union(7, 6);
	u.Union(7, 8);

	u.Union(1, 4);
	u.Union(4, 9);

	u.Union(2, 3);
	u.Union(2, 5);

	std::cout << u.SetCount() << std::endl;
}

 另外说下压缩路径的问题,当并查集的数据非常大时,我们要找到这个元素的根,可能就需要向上找很久,有一种解决方案就是,在每次查找的时候,如果它的父亲结点不是根结点的话,就将它放到根结点的儿子结点。这样就能减少遍历的次数。

  不过压缩路径通常还是只有在数据量非常大的情况下使用。

并查集虽然是一种数据结构,但是有时候又可以是一种解决问题的思路。

比如这道题,可以直接手搓一个简单的并查集,很容易就秒掉。

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        vector<int> set(n,-1);
        auto findRoot = [&set](int x)
        {
            while(set[x] >= 0)
                x = set[x];
            return x;
        };

        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < n; j++)
            {
                if(isConnected[i][j] == 1)
                {
                    int root1 = findRoot(i);
                    int root2 = findRoot(j);
                    if(root1 != root2)
                    {
                        set[root1] += set[root2];
                        set[root2] = root1;
                    }
                }
            }
        }

        int count = 0;
        for(auto &x : set)
        {
            if(x < 0)
                count++;
        }

        return count;
    }
};

 总结并查集的特点:

1.一个位置的值是负数,那么它就是树的根,这个负数的绝对值就是这个树结点的个数。

2.一个位置的值是正数,那么这个正数就是它父亲结点的下标。


http://www.kler.cn/a/233536.html

相关文章:

  • INQUIRE:一个包含五百万张自然世界图像,涵盖10,000个不同物种的专为专家级文本到图像检索任务设计的新型基准数据集。
  • 利用阿里云下载 WebRTC 源码
  • DNS面临的4大类共计11小类安全风险及防御措施
  • 鸿蒙自定义UI组件导出使用
  • Xshell,Shell的相关介绍与Linux中的权限问题
  • sql专题 之 sql的执行顺序
  • 力扣231. 2 的幂(数学,二分查找,位运算)
  • H5/CSS 笔试面试考题(61-70)
  • TCP 传输控制协议——详细
  • Java强训day16(选择题编程题)
  • 【项目问题解决】java. net.SocketException: Connection reset
  • python命令行参数Argparse
  • Django(十)
  • rtt设备io框架面向对象学习-框架
  • 探索C语言的内存魔法:动态内存管理解析
  • 6 scala-面向对象编程基础
  • [word] word2019段落中创建纵横混排的方法图解教程 #知识分享#其他#职场发展
  • google scholar引用出现问题
  • 上位机图像处理和嵌入式模块部署(利用python开发软件)
  • PYTHON 120道题目详解(73-75)
  • MySQL数据库语句总结
  • jvm问题自查思路
  • 【开源】SpringBoot框架开发超市账单管理系统 JAVA+Vue+SpringBoot+MySQL
  • ideaIU-2023.2.1安装教程
  • 【ROS机器人系统】实验 2 熟悉和使用 URDF 创建机器人模型
  • 分享76个表单按钮JS特效,总有一款适合您