990.等式方程的可满足性·并查集

本文介绍了并查集的基础知识及其在LeetCode问题中的应用,通过建立元素集合并使用并查集的合并和查询操作判断等式是否满足。并查集通过路径压缩和按秩合并优化,提高了查询效率。代码示例展示了如何用C++实现并查集,并用于解决等式不等式判断问题。
摘要由CSDN通过智能技术生成

 关于并查集的基础知识

http://t.csdn.cn/BfQFIhttp://t.csdn.cn/BfQFI

题目

 力扣https://leetcode.cn/problems/satisfiability-of-equality-equations/solution/c-by-xun-ge-v-e5en/

示例

思路

对于本题我们可以建立模型,把所有元素划分到若干个不相交的集合中,每个集合里的人彼此是相互独立的。当两个元素之间用==连接说明这两个集合是属于同一集合,判断两个元素是否满足!=,只需看它们是否属于同一个集合即可。因此,这里就可以考虑用并查集进行维护了。
并查集主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
合并(Union):把两个不相交的集合合并为一个集合。
查询(Find):查询两个元素是否在同一个集合中。

初始化
假如有编号为1, 2, 3, ..., n的n个元素,我们用一个数组fa[]来存储每个元素的父节点(因为每个元素有且只有一个父节点,所以这是可行的)。一开始,我们先将它们的父节点设为自己。
查询
我们用递归的写法实现对代表元素的查询:一层一层访问父节点,直至根节点(根节点的标志就是父节点是本身)。要判断两个元素是否属于同一个集合,只需要看它们的根节点是否相同即可。
合并优化
在合并过程中如果并对其进行优化,可能会使得树的高度变得非常大,使得查询过程非常长,于是我们将每次合并的元素都指向他们合并之后的根节点,这样子树的高度会降低,因为我们只关心他们是不是在一个组中,并不关心组中有哪些成员变量
有些人可能有一个误解,以为路径压缩优化后,并查集始终都是一个菊花图(只有两层的树的俗称)。但其实,由于路径压缩只在查询时进行,也只压缩一条路径,所以并查集最终的结构仍然可能是比较复杂的。
我们用一个数组rank[]记录每个根节点对应的树的深度(如果不是根节点,其rank相当于以它作为根节点的子树的深度)。一开始,把所有元素的rank(秩)设为1。合并时比较两个根节点,把rank较小者往较大者上合并。

代码

typedef struct bcj{
    int * fa;
    int * rand;
}bcj;

//初始化
bcj * bcjCreateInit(int n)
{
    bcj * obj = malloc(sizeof(bcj));
    obj->fa = malloc(sizeof(int) * n);
    obj->rand = malloc(sizeof(int) * n);
    for(int i = 0; i < n; i++)
    {
        obj->fa[i] = i;
        obj->rand[i] = 1;
    }
    return obj;
}
//查询根节点
int find(bcj * obj, int x)
{
    if(obj->fa[x] == x)
        return x;
    else
        return find(obj,obj->fa[x]);
}
//合并
/*合并优化:
1 路径压缩:1、隔代压缩 (this.parent = this.parent.parent)2、完全压缩 (this.parent = root)
2 按秩合并
这里用按秩合并*/
void merge(bcj * obj, int i, int j)
{
    int x = find(obj, i), y = find(obj, j);
    if (obj->rand[x] <= obj->rand[y])//那边高度高,插入到那边
        obj->fa[x] = y;
    else
        obj->fa[y] = x;
    if (obj->rand[x] == obj->rand[y] && x != y)
        obj->rand[y]++;
    return;
}
//销毁
void freeBcj(bcj * obj)
{
    free(obj->fa);
    free(obj->rand);
    free(obj);
    return;
}
//将equations转化为对应并查集
/*
*bool equationsPossible(char ** equations, int equationsSize)
bool equationsPossible:检查关系是否成立
char ** equations元素数组
int equationsSize:元素行数
返回值:成立返回true,否则返回false
*/
bool equationsPossible(char ** equations, int equationsSize){
    bcj * obj = bcjCreateInit(26);
    for(int i = 0; i < equationsSize; i++)
    {
        if(equations[i][1] == '=')//加入并查集
        {
            merge(obj, equations[i][0] - 'a', equations[i][3] - 'a');
        }
    }
    for(int i = 0; i < equationsSize; i++)
    {
        if(equations[i][1] == '!')//判断是不是在一个组,在一个组那么!=不成立
        {
            int left = find(obj, equations[i][0] - 'a');
            int right = find(obj, equations[i][3] - 'a');
            if(left == right)
            {
                return false;
            }
        }
    }
    freeBcj(obj);
    return true;
}

时间空间复杂度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值