首页 > 其他分享 >并查集(原理、实现、应用)

并查集(原理、实现、应用)

时间:2024-11-09 13:18:12浏览次数:5  
标签:元素 数组 int 查集 应用 集合 原理 ufs size

一、原理

在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。

开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。

在此过程中要反复用到查询某一个元素归属于那个集合的运算。

适合于描述这类问题的抽象数据类型称为并查集(union-find set)。

image-20241108233412096

例子:假设有一个班级中有10个学生,每个学生最初属于一个独立的集合。

随着学生之间的互动,他们可能会形成新的集合或加入到已有的集合中。

例如:

  1. 学生A和学生B成为朋友,他们属于同一个集合。

  2. 学生C和学生D也成为朋友,他们属于另一个集合。

  3. 学生A和学生C互动,他们所在的集合合并。

1

image-20241108233446974

image-20241108233509640

结论:

  1. 数组的下标对应集合中元素的编号

  2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数

  3. 数组中如果为非负数,代表该元素双亲在数组中的下标

二、作用

并查集一般可以解决一下问题:

  1. 查找元素属于哪个集合

沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)

  1. 查看两个元素是否属于同一个集合

沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在

  1. 将两个集合归并成一个集合

将两个集合中的元素合并,将一个集合名称改成另一个集合的名称

  1. 集合的个数

遍历数组,数组中元素为负数的个数即为集合的个数。

三、实现

UnionFindSet.h

#pragma once
​
#include<vector>
#include<map>
​
//template<class T>
//class UnionFindSet
//{
//public:
//  UnionFindSet(const T* a, size_t n)
//  {
//      for (size_t i = 0; i < n; i++)
//      {
//          _a.push_back(a[i]);
//          _indexMap[a[i]] = i;
//      }
//  }
//private:
//  vector<T> _a;         //编号找人
//  map<T, int> _indexMap;//人找编号
//};
​
​
//      0       |       1       |       2
//6     7   8   |   4       9   |   3       5
​
//  0   1   2   3   4   5   6   7   8   9
// -4  -3  -3   2   1   2   0   0   0   1
// -7   0  -3   2   1   2   0   0   0   1
​
​
//1. 数组的下标对应集合中元素的编号
//2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
//3. 数组中如果为非负数,代表该元素双亲在数组中的下标
​
​
class UnionFindSet
{
public:
    UnionFindSet(size_t n)
        :_ufs(n,-1)
    {}
​
    void Union(int x1, int x2)
    {
        int root1 = Findroot(x1);
        int root2 = Findroot(x2);
​
        //如果在同一集合就不用合并
        if (root1 == root2)
        {
            return;
        }
        if (abs(_ufs[root1]<abs(_ufs[root2])))
        {
            swap(root1, root2);
        }
​
        //2合并进1中
        _ufs[root1] += _ufs[root2];
        _ufs[root2] = root1;
    }
​
    int Findroot(int x)
    {
        int root = x;
        while (_ufs[root] >= 0)
        {
            root = _ufs[root];
        }
​
        while (_ufs[x] >= 0)
        {
            int parent = _ufs[x];
            _ufs[x] = root;
            x = parent;
        }
        return root;
    }
​
    bool InSet(int x1, int x2)
    {
        return Findroot(x1) == Findroot(x2);
    }
​
    size_t SetSize()
    {
        size_t size;
        for (size_t i = 0; i < _ufs.size(); i++)
        {
            if (_ufs[i] < 0)
            {
                ++size;
            }
        }
        return size;  
    }
​
private:
    vector<int> _ufs;
};
​
void TestUnionFindSet()
{
    UnionFindSet ufs(10);
    ufs.Union(8, 9);
    ufs.Union(7, 8);
    ufs.Union(6, 7);
    ufs.Union(5, 6);
    ufs.Union(4, 5);
​
    ufs.Findroot(9);
}

四、应用

1.LRC 116.省份数量

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        vector<int> ufs(isConnected.size(),-1);
​
        auto findRoot=[&ufs](int x)
        {
            while(ufs[x]>=0)
            {
                x=ufs[x];
            }
            return x;
        };
​
        for(size_t i=0;i<isConnected.size();i++)
        {
            for(size_t j=0;j<isConnected[i].size();j++)
            {
                if(isConnected[i][j]==1)
                {
                    int root1=findRoot(i);
                    int root2=findRoot(j);
                    if(root1!=root2)
                    {
                        ufs[root1]+=ufs[root2];
                        ufs[root2]=root1;
                    }
                }
            }
        }
        int n=0;
        for(auto e:ufs)
        {
            if(e<0)
            {
                ++n;
            }
        }
​
        return n;
    }
};

2. 990.等式方程的可满足性

class Solution {
public:
    bool equationsPossible(vector<string>& equations) {
        vector<int> ufs(26,-1);
        auto findroot=[&ufs](int x)
        {
            while(ufs[x]>=0)
            {
                x=ufs[x];
            }
            return x;
        };
​
        //第一遍 找相等的放在同一集合
        for(auto &str:equations)
        {
            if(str[1]=='=')
            {
                int r1=findroot(str[0]-'a');
                int r2=findroot(str[3]-'a');
                if(r1!=r2)
                {
                    ufs[r1]+=ufs[r2];
                    ufs[r2]=r1;
                }
            }
        }
​
​
        //第二遍 看不相等的在不在一个集合 在就相勃了
        //返回false
        for(auto &str:equations)
        {
            if(str[1]=='!')
            {
                int r1=findroot(str[0]-'a');
                int r2=findroot(str[3]-'a');
                if(r1==r2)
                {
                   return false;
                }
            }
        }
        return true;
    }
};

标签:元素,数组,int,查集,应用,集合,原理,ufs,size
From: https://blog.csdn.net/lll_666666/article/details/143643714

相关文章

  • c++-有关输出、信息输入、趣味输入应用、运算符、变量、浮点数数据类型的基础知识
    C++是一种功能强大且广泛使用的编程语言,它可以用于开发各种类型的应用程序。在这篇文章中,我们将介绍C++程序的输出、信息输入、趣味输入应用、运算符、变量和浮点数数据类型的基础知识。目录输出信息输入趣味输入应用运算符变量浮点数数据类型题目题目1:解答1:题目2:......
  • 深入Java多态机制:从原理到实现
    目录1.什么是多态?2.如何在Java中实现多态?2.1方法重写实现多态2.2接口实现多态3.Java接口中方法实现的支持3.1默认方法4.总结多态(Polymorphism)是面向对象编程(OOP)的核心概念之一。多态允许对象在不同的上下文中执行不同的行为,即同一操作可以在不同的对象中产生不......
  • 上拉电阻应用原则
    1、当TTL电路驱动COMS电路时,如果TTL电路输出的高电平低于COMS电路的最低高电平(一般为3。5V),这时就需要在TTL的输出端接上拉电阻,以提高输出高电平的值。……………………..2、OC门电路“必须加上拉电阻,才能使用”。3、为加大输出引脚的驱动能力,有的单片机管脚上也常使用上拉......
  • GoLang协程Goroutiney原理与GMP模型详解
    本文原文地址:GoLang协程Goroutiney原理与GMP模型详解什么是goroutineGoroutine是Go语言中的一种轻量级线程,也成为协程,由Go运行时管理。它是Go语言并发编程的核心概念之一。Goroutine的设计使得在Go中实现并发编程变得非常简单和高效。以下是一些关于Goroutine的关键特性:轻量......
  • 一文彻底弄懂JUC工具包的CountDownLatch的设计理念与底层原理
    CountDownLatch是Java并发包(java.util.concurrent)中的一个同步辅助类,它允许一个或多个线程等待一组操作完成。一、设计理念CountDownLatch是基于AQS(AbstractQueuedSynchronizer)实现的。其核心思想是维护一个倒计数,每次倒计数减少到零时,等待的线程才会继续执行。它的主要设......
  • Docker版的应用不要连127.0.0.1
    昨晚一直在配置docker版的nacos,使用如下命令,然后一直启动不成功dockerrun-d--envMODE=standalone--namenacos--restart=always-eSPRING_DATASOURCE_PLATFORM=mysql-eMYSQL_DATABASE_NUM=1-eMYSQL_SERVICE_HOST=127.0.0.1-eMYSQL_SERVICE_PORT=3306-eMYSQL_SERV......
  • SciTech-Mathmatics-BigDataAIML: PCA(Principle Component Analysis)主成分分析 的
    SciTech-Mathmatics-BigDataAIML:PCA(PrincipleComponentAnalysis)主成分分析参考链接HowtoCalculatePrincipalComponentAnalysis(PCA)fromScratchinPythonhttps://www.kaggle.com/code/aurbcd/pca-using-numpy-from-scratchPCAusingNumpyfromscratchhttps......
  • YOLOv8目标检测、跟踪、图像分割和姿态估计应用程序+Streamlit制作的用户界面
    YOLOv8多功能应用开发指南在当今的计算机视觉领域,YOLO(YouOnlyLookOnce)系列模型以其快速而准确的目标检测能力闻名。随着技术的进步,YOLOv8不仅继承了前代模型的优点,还进一步增强了性能,并引入了新的功能如目标跟踪、图像分割及姿态估计。本篇将详细介绍如何基于YOLOv8构......
  • IDEA2023应用第一部分 环境配置(摘自CSDN 作者:生活需要淡定)
    第一部分环境配置1.1语言设置1.打开IntelliJIDEA,‌进入菜单栏的File->Settings。‌2.在弹出的设置窗口中,‌点击Plugins,‌然后在搜索框输入Chinese。‌3.找到Chinese(Simplified)Language插件,‌点击Install进行安装。‌4.安装完成后,‌重启IntelliJIDEA,‌即可看到界面......
  • vue3组件应用 + 以及组件相关知识应用
    文章目录vue组件化开发一、什么是Vue组件化开发二、组件的创建方式三、组件的数据传递四、组件的生命周期五、组件的插槽(Slot)数据传递的方式实例组件生命周期应用场景插槽应用define相关应用vue组件化开发一、什么是Vue组件化开发概念Vue组件化开发是一种将用......