首页 > 其他分享 >并查集

并查集

时间:2023-10-18 20:00:56浏览次数:41  
标签:int 查集 us unionset 集合 root1 root2


并查集的原理

来自百度百科

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始的时候让每个元素构成单个元素的集合,然后按一定顺序讲属于同一组的元素所在集合合并,期间要反复查询一个元素在哪个集合中。描述改问题的抽象数据结构为并查集。 并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

对于并查集的操作

  1. 初始化:对于初始化,我们都把每个元素初始化为-1。在并查集中,负数表示根节点,它的绝对值表示以它为根的集合有多少元素。非负表示根节点的下标
  2. 查找:查找元素所在的集合,即根节点
  3. 合并:将两个元素所在的集合合并为一个集合。在合并之前要判断两个元素是否属于同一个集合。


并查集的实现

对于并查集的实现我们就要先确定它的存储类型——即到底用什么存储它
这里我们用数组的形式进行存储——类似于堆的存储形式
那么对于该数据结构,其成员变量设成vector<int>注意的是:下标是实际的值,存储的是根节点的下标或者是以该节点为根的个数

#include <vector>

class UnionSet
{
	vector<int> _us;
public:
	UnionSet(const int size)
		:_us(size,-1){}
	//
};

成员方法:查找
查找就是查找根节点——负数就是根——返回是下标

int Find(int x)
{
    while (_us[x] >= 0)
    {
        x = _us[x];
    }
    return x;
}

成员方法:合并
把两个集合进行合并,进行合并那么就要找到该值对应的根,然后进行下面的操作
可以以根所对应的那个为新集合的根,根所对应的值大的为孩子。
操作方法就是值相加给小的,大的指向小的
同时要注意:如果是属于同一个集合

void Union(int x,int y)
{
    int root1 = Find(x);
    int root2 = Find(y);
    if (root1 < root2) {
        _us[root1] += _us[root2];
        _us[root2] = root1;
    }
    else if (root1 > root2) {
        _us[root2] += _us[root1];
        _us[root1] = root2;
    }
}

计算有多少个集合——只需要查看有几个小于0的就可以了

int SetCount()
{
    int ret = 0;
    for (auto x : _us)
    {
        if (x < 0)
            ret++;
    }
    return ret;
}

路径压缩,以后再写

时间复杂度O(N)

并查集的应用

省份数量:
https://leetcode.cn/problems/bLyHh0/description/

class UnionSet
{
	vector<int> _us;
public:
	UnionSet(const int size)
		:_us(size,-1){}
	//
	int Find(int x)
	{
		while (_us[x] >= 0)
		{
			x = _us[x];
		}
		return x;
	}
	void Union(int x,int y)
	{
		int root1 = Find(x);
		int root2 = Find(y);
		if (root1 < root2) {
			_us[root1] += _us[root2];
			_us[root2] = root1;
		}
		else if (root1 > root2) {
			_us[root2] += _us[root1];
			_us[root1] = root2;
		}
	}
	int SetCount()
	{
		int ret = 0;
		for (auto x : _us)
		{
			if (x < 0)
				ret++;
		}
		return ret;
	}
};
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n=isConnected.size();
        UnionSet uset(n);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(isConnected[i][j]==1){
                    uset.Union(i,j);
                }
            }
        }
        return uset.SetCount();
    }
};

其实按上面那么写不是很方便,还有单独弄一个类。
可以用下面的方法:其实就是把上面的类进行拆分了

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
			int n = isConnected.size();
			vector<int> unionset(n,-1);
			auto f=[&](int i,int j){
				while(unionset[i]>=0)
				i=unionset[i];
				while(unionset[j]>=0)
				j=unionset[j];
				if(i!=j)
				{
					unionset[i]+=unionset[j];
					unionset[j]=i;	
				}
			};
			//自己写一个合并
			for(int i=0;i<n;i++)
			{
				for(int j=0;j<n;j++)
				{
					if(isConnected[i][j]==1)
						f(i,j);
				}
			}
			//自己写一个查询
			int ret=0;
			for(auto x:unionset)
			{
				if(x<0)
				ret++;
			}
			return ret;
    }
};

等式方程的可满足性

并查集_并查集


https://leetcode.cn/problems/satisfiability-of-equality-equations/description/

  1. 先把相等的到放入同一个集合,把所有的集合构造完成
  2. 不相等的进行查询是不是属于同一个集合,如果属于就为false
class Solution {
public:
    bool equationsPossible(vector<string>& equations) {
        vector<int> unionset(26,-1);
        auto find=[&](int i){
            while(unionset[i]>=0){
                i=unionset[i];
            }
            return i;
        };
        for(auto& str:equations)
        {
            int root1 = find(str[0]-'a');
            int root2 = find(str[3]-'a');
            if(str[1]=='='){
                if(root1!=root2)
                {
                    unionset[root1]+=unionset[root2];
                    unionset[root2]=root1;
                }
            }
        }
        for(auto& str:equations)
        {
            int root1 = find(str[0]-'a');
            int root2 = find(str[3]-'a');
            if(str[1]=='!'){
                if(root1==root2)
                return false;
            }
        }
        return true;
    }
};

标签:int,查集,us,unionset,集合,root1,root2
From: https://blog.51cto.com/u_15869810/7922533

相关文章

  • 并查集学习指南
    前置芝士并查集思想[find][python]#pythonwhiledeffind(x:int)->int: whilex!=fa[x]: x=fa[x]=fa[fa[x]] returnx#python递归deffind(x:int)->int: iffa[x]!=x: fa[x]=find(fa[x]) returnfa[x][c++]//c++whilelambda/*function<int(int)>fi......
  • [算法分析与设计] 3. 并查集分析与反阿克曼函数
    Union-Find问题:给定\(n\)个元素,最初每个元素在一个集合中,有两种操作,union表示合并两个集合,find表示查询某个特定元素所在的集合。并查集是一种数据结构。其为每个集合寻找一个代表元,代表元可以是任意的,也可以随操作变化,但需要满足任何时刻一个集合的代表元是确定且唯一的。......
  • 并查集
    并查集如果有一个关系网,我们需要建立两点之间的关系,如果仅用一维链表,则有可能无法考虑到两点同为一点“儿子”的情况,则我们需要建立一个方式,从而直接对比两点“祖父”。一.递归查询intfind(intx){ if(fa[x]==x)returnx;//只有祖父自环,如果他也自环,则找到父节点 ......
  • 并查集的实现【学习算法】
    并查集的实现【学习算法】前言版权推荐并查集的实现最后前言2023-9-2614:38:02以下内容源自《【学习算法】》仅供学习交流使用版权禁止其他平台发布时删除以下此话本文首次发布于CSDN平台作者是CSDN@日星月云博客主页是禁止其他平台发布时删除以上此话推荐算法讲解056【必备】并......
  • 并查集
    基本的并查集OI-wikiLink并查集,一种用于管理元素所属集合的数据结构,形如一片森林,同一棵树内的元素所属同一集合,不同树内的元素不属于同一集合。将并查集拆分一下。并,合并;查,查询;集,处理的是集合相关内容。所以并查集一共有两种操作:合并两元素对应集合、查询某元素所属集合(查......
  • 数据结构-并查集
    并查集的使用范围:1.合并集合2.查询两元素是否属于同一集合   高级用法:  3.进行集合划分<带权并查集>  4.连通块块数查询&块内元素个数统计<连通图>  5.撤销合并<可持久化并查集>//本文暂不涉及,我还不会并查集基本操作:#definerep(i,n)for(int......
  • 线段树分治&可撤销并查集
    可撤销并查集按时间顺序用一个栈维护合并信息,撤销时从栈顶弹出合并信息,恢复原状态。并查集查找祖先时不能路径压缩,只能按秩合并。例题:[ABC302Ex]BallCollector容易想到将\(A_i\)和\(B_i\)之间连边。遍历整棵树,用可撤销并查集维护图。为了进一步求得答案,需要注意到该......
  • 可持久化非确定状态AC自动分块维护线段平衡仙人掌优化最小费用最大流预处理混合图上莫
    P8946TheLostSymbol这种类型的dp的特点就是大部分转移形如\(f(i,j)\rightarrowf(i+1,j+1)\)之类的,并且当以上转移出现时原数组被清空,这就可以用一个deque来维护,然后对于全局赋值/全局加,需要对每个位置维护一个时间戳,并记录上一次赋值/加是什么时候,以便标记下传。(貌似......
  • 倍增并查集
    假如说我们有\(n\)个元素,\(m\)次操作。每次操作给定\(x,y,z\),要求对于任意\(0\lei\lez\),将\(x+i\)和\(y+i\)合并,求最后的并查集形态。数据范围是\(10^5\)级别的。我们考虑将\(z\)二进制拆分,那么可以将\(z\)分解为\(O(\logn)\)个\(2\)的整数次幂之和,也就可......
  • poj 1182 食物链---带权值的并查集
    这题就一组数据,用while(scnaf(“%d%d”,&n,&m)!=EOF)..就wa了,我wa了数次,无语了。。带权值的并查集,d[]数组存的是每个点和根节点的关系,同类为d[i]=0; 根节点吃i点为d[i]=1; i点吃根节点为d[i]=2;自己画图感受一下吧!!#include<stdio.h>#include<string.h>#include<stdlib.h>in......