并查集原理分析

Posted ych9527

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集原理分析相关的知识,希望对你有一定的参考价值。

1.并查集是什么

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

2.并查集性质

image-20210714212416244

  • 由上图可知,并查集有如下几点性质
    • 数组下标对应集合中元素的编号
    • 根下标对应的内容为负数
    • 子元素下标对应的内容为根的下标(子元素下标对应的内容非负数)

3.并查集可以解决的问题

  • 查找元素属于哪个集合
    • 查找对应元素的根
  • 查看两个元素是否属于同一个集合
    • 判断两个元素的根是否相等
  • 将两个集合归并成一个集合
    • 将两个元素的根进行合并
  • 集合的个数
    • 查找根的个数

4.并查集模板

#include "test.h"

class UnionFindSet
{
public:
	UnionFindSet(int size)//传入元素的个数
	{
		_arr.resize(size, -1);
	}

	int FindRoot(int x)//给定一个下标寻找它的根
	{

		while (_arr[x] >= 0)//大于等于0,表示里面存储的是根的下标
		{
			x = _arr[x];
		}
		return x;
	}

	bool Union(int x1, int x2)//将两个下标对应的根,进行合并
	{
		//找到两个根的位置
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		
		if (root1 == root2)
			return false;

		_arr[root1] += _arr[root2];
		_arr[root2] = root1;

		return true;
	}

private:
	vector<int> _arr;
};

5.并查集的应用

省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。返回矩阵中 省份 的数量。

题解:

  • 典型的并查集题目
  • 首先每个为1的城市,本身都是一颗树,但是他们记录的下标表示两个城市相连,即将这两颗树相连
  • 最后统计并查集中根的数量,返回即可 -> 根的数量就是省的数量
class Solution {
public:

class UnionFindSet
{
public:
	UnionFindSet(int size)
	{
		_arr.resize(size, -1);
	}

	int FindRoot(int x)//给定一个下标寻找它的根
	{
		while (_arr[x] >= 0)//大于等于0,表示里面存储的是根的下标
		{
			x = _arr[x];
		}
		return x;
	}

	bool Union(int x1, int x2)//将两个下标对应的根,进行合并
	{
		//找到两个根的位置
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);
		
		if (root1 == root2)
			return false;

		_arr[root1] += _arr[root2];
		_arr[root2] = root1;

		return true;
	}

    int Size()
    {
        int n=0;
        for(auto&e:_arr)
        {
            if(e<0)//为根
            n++;
        }
        return n;
    }

private:
	vector<int> _arr;
};
    int findCircleNum(vector<vector<int>>& isConnected) {
        UnionFindSet uft(isConnected.size());

        for(int i=0;i<isConnected.size();i++)
        {
            for(int j=0;j<isConnected.size();j++)
            {
                if(isConnected[i][j]==1)
                {
                    uft.Union(i,j);
                }
            }
        }

        return uft.Size();
    }
};

等式方程的可满足性

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:“a==b” 或 “a!=b”。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。

题解:

  • 如果两个字符相等,则放入同一个集合之中 -> 即他们的根相同
  • 如果两个字符不相等,则去集合之中查找他们的根 -> 根相同则返回false
class Solution {
public:
    class UnionFindSet
    {
    private:
        vector<int> arr;
    public:
        UnionFindSet(int n)
        {
            arr.resize(n,-1);
        }

        int FindRoot(int x)//寻找根
        {
            while(arr[x]>=0)//表示不是根
            {
                x=arr[x];
            }
            return x;
        }

        bool Union(int x,int y)
        {
            int root1=FindRoot(x);
            int root2=FindRoot(y);
            if(root1==root2)
            return false;

            arr[root1]+=arr[root2];
            arr[root2]=root1;
            return true;
        }
    };
    bool equationsPossible(vector<string>& equations) {
        //将相等的都放入一个集合之中
        //然后判断不相等的在不在一个集合-> 根相不相同

        UnionFindSet ufs(26);

        for(auto&e:equations)
        {
            if(e[1]=='=')
            ufs.Union(e[0]-'a',e[3]-'a');
        }
        for(auto&e:equations)
        {
            if(e[1]!='=')
            {
                int root1=ufs.FindRoot(e[0]-'a');
                int root2=ufs.FindRoot(e[3]-'a');
                if(root1==root2)
                    return false;
            }
        }
      return true;
    }
};

以上是关于并查集原理分析的主要内容,如果未能解决你的问题,请参考以下文章

并查集的原理及实现

树--12---并查集

「并查集」程序自动分析

并查集分析与扩展

数据结构高阶第十一篇——并查集(原理+实现+应用)

数据结构高阶第十一篇——并查集(原理+实现+应用)