当前位置: 首页 > 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/news/233536.html

相关文章:

  • 力扣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特效,总有一款适合您
  • 07 A B 从计数器到可控线性序列机
  • 13 OpenGL顶点后处理
  • DataX详解和架构介绍
  • JavaWeb- 转发(Forward)和重定向(Redirect)
  • [韩顺平]python笔记
  • Linux系统基础 03 IP地址虚拟网络、Linux软件包管理、ssh服务、apache服务和samba服务的简单搭建
  • 构建高效Docker环境:网络配置全指南
  • 《CSS 简易速速上手小册》第3章:CSS 响应式设计(2024 最新版)
  • 企业飞书应用机器人,使用python自动发送文字内容到群消息
  • Linux增删ip