高级数据结构 | 并查集

并查集原理

       在一些应用问题中,需要 n 个不同的元素划分成一些不相交的集合 开始时,每个元素自成一个单元素集 合,然后按一定的规律将归于同一组元素的集合合并 。在此过程中 要反复用到查询某一个元素归属于那个集 合的运算 。适合于描述这类问题的抽象数据类型称为 并查集 (union-fifind set)
并查集一般可以解决一下问题:
1. 查找元素属于哪个集合
        沿着数组表示树形关系以上一直找到根 ( 即:树中中元素为负数的位置 )
2. 查看两个元素是否属于同一个集合
       沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
3. 将两个集合归并成一个集合
       将两个集合中的元素合并
       将一个集合名称改成另一个集合的名称
4. 集合的个数
       遍历数组,数组中元素为负数的个数即为集合的个数。

并查集实现

class UnionFindSet
{
public:
    UnionFindSet(int n) : _ufs(n, -1) {}

    //4. 集合的个数
    //遍历数组,数组中元素为负数的个数即为集合的个数
    int count() const
    {
        int count = 0;
        for (int i : _ufs)
            if (i < 0)
                ++count;
        return count;
    }
    //1. 查找元素属于哪个集合
    //沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)
    int findRoot(int index)
    {
        while (_ufs[index] >= 0)
        {
            index = _ufs[index];
        }
        return index;
    }

    //2. 查看两个元素是否属于同一个集合
    //沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
    bool isUnion(int x1, int x2)
    {
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        return index1 == index2;
    }

    //3. 将两个集合归并成一个集合
    //将两个集合中的元素合并
    //将一个集合名称改成另一个集合的名称
    bool unionSet(int x1, int x2)
    {
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1 == index2)
            return false;
        _ufs[index1] += _ufs[index2];
        _ufs[index2] = index1;
        return true;
    }

private:
    vector<int> _ufs;
};
并查集应用

1.等式方程的可满足性 

链接:https://leetcode-cn.com/problems/satisfiability-of-equality-equations

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。

只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。

class Solution {
public:
    bool equationsPossible(vector<string>& equations) 
    {
        UnionFionSet us(26);
        for(auto &str:equations)
            if(str[1]=='=')
                us.unionSet(str[0]-'a',str[3]-'a');

        for(auto &str:equations)
            if(str[1]=='!'&&us.isUnion(str[0]-'a',str[3]-'a'))
                return false;
        return true;
    }
};

2.朋友圈   

链接:https://leetcode-cn.com/problems/bLyHh0

一个班上有 n 个同学,其中一些彼此是朋友,另一些不是。朋友关系是可以传递的,如果 a 与 b 直接是朋友,且 b 与 c 是直接朋友,那么 a 与 c 就是间接朋友。

定义 朋友圈 就是一组直接或者间接朋友的同学集合。

给定一个 n x n 的矩阵 isConnected 表示班上的朋友关系,其中 isConnected[i][j] = 1 表示第 i 个同学和第 j 个同学是直接朋友,而 isConnected[i][j] = 0 表示二人不是直接朋友。

返回矩阵中 朋友圈的数量。

class Solution 
{
public:
    int findCircleNum(vector<vector<int>>& isConnected) 
    {
        UnionFionSet us(isConnected.size());
        for(int i = 0;i<isConnected.size();++i)
        {
            for(int j = 0;j<isConnected[i].size();++j)
            {
                if(i==j)
                    continue;
                if(isConnected[i][j]==1)
                    us.unionSet(i,j);
            }
        }
        return us.count();
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值