leetcode990. 等式方程的可满足性

leetcode990. 等式方程的可满足性

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

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

示例 1:

输入:["a==b","b!=a"]
输出:false
解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程

示例 2:

输出:["b==a","a==b"]
输入:true
解释:我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。

示例 3:

输入:["a==b","b==c","a==c"]
输出:true

示例 4:

输入:["a==b","b!=c","c==a"]
输出:false

示例 5:

输入:["c==c","b==d","x!=z"]
输出:true

提示:

  • 1 <= equations.length <= 500
  • equations[i].length == 4
  • equations[i] [0] 和 equations[i] [3] 是小写字母
  • equations[i] [1] 要么是 ‘=’,要么是 '!'
  • equations[i] [2] 是 '='

方法:并查集

思路:

这道题是一道并查集的题目,刚开始我也是很懵,首先看一下并查集。

这里引用csdn上面的一篇文章中的开头:[https://blog.csdn.net/dingdingdodo/article/details/104290972]:

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题,常常在使用中以森林来表示。并查集通常用来解决管理若干元素的分组问题

并查集可以高效完成下列操作 (并与查的功能) :

并:合并元素a和元素b所在的组
查:查询元素a和元素b是否属于同一组
并查集的结构

并查集使用树形结构形成,实际上可以看作是森林

如图所示,我们可以将左侧的分组以右侧的森林的形式表示。其中每个元素对应树中的一个节点,每一个组对应着森林中的一颗树,在并查集中,通常我们不在意父节点与子节点的顺序,实际上树的形状也无关紧要,只需要令同一组的元素对应到同一个树上即可。
————————————————
版权声明:本文为CSDN博主「coding丁」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dingdingdodo/article/details/104290972

由上面可知,并查集相当于森林,将相同的连通分量聚集在一起,而不关心它们的顺序,因为他们是一样的。**相同的连通分量使用一个元素来作为代表,也就相当于根节点。**而并查集的一个优点是它可以通过数组或字典(哈希表)来实现,并不需要定义树结构。

以本题为例,由于变量只能为小写字母,也就是’a’…‘z’,我们当然可以通过字典,键为这个变量,值为这个变量的父节点(不一定是这个森林的代表——根节点);但是我们更简单的使用数组来表示,由于小写字母ASCII码减去’a‘的ASCII码范围在[0,25],因此我们定义一个长度为26的数组即可,首先假设它们都不一样,也就是有26个森林,设这个数组parent[i] = i,也就是使用自己代表自己。

然后我们需要考虑查找操作,对于找到某个元素i的根节点(代表元素),如果parent[i] = = i,那么就是自己,如果不是,那么继续递归parent[parent[i]]…直到找到某个值x,parent[x] = x,就是根节点了。这里我们可以做一个优化,叫做路径压缩。

如上图所示,我们在找4的根节点的时候,需要向上遍历到3,2,才到1,这样如果下一次再需要找到这个4的时候,我们还是需要遍历一次,因此我们使用路径压缩,遍历第一次的时候,遍历到1之后,就将4的父节点从原来的3改为这个根节点1,这样下次再查找的时候就可以一下子找到了。

我们再考虑合并操作,这个其实很简单,对于两个元素,只要调用上面的find查找到它们对应的根节点,将其中一个的根节点的父节点,改为另一个根节点即可,就相当于合并成了以第二个根节点为根的森林。

并查集的定义代码如下:

class UnionFind:
    #初始化
    def __init__(self):
        self.parent = [k for k in range(26)]
    #查找某个元素的根节点
    def find(self,index):
        if self.parent[index] == index:
            return index
        #递归进行路径压缩
        self.parent[index] = self.find(self.parent[index])
        return self.parent[index]
    #合并两个下标对应的“森林”
    def union(self,index1,index2):
        self.parent[self.find(index2)] = self.find(index1)

对于这道题,我们首先初始化并查集,然后遍历表达式中为==的,将这些合并,然后遍历这些表达式中为!=的,找到两个元素的根节点,如果它们的根节点一致,说明它们是连通分量,在一个森林中,那么显然不符合!=,所以返回False,所有表达式都符合条件,返回True。

代码:

class UnionFind:
    #初始化
    def __init__(self):
        self.parent = [k for k in range(26)]
    #查找某个元素的根节点
    def find(self,index):
        if self.parent[index] == index:
            return index
        #递归进行路径压缩
        self.parent[index] = self.find(self.parent[index])
        return self.parent[index]
    #合并两个下标对应的“森林”
    def union(self,index1,index2):
        self.parent[self.find(index2)] = self.find(index1)

class Solution:
    def equationsPossible(self, equations: List[str]) -> bool:
        #初始化并查集
        uf = UnionFind()
        #便利==表达式,完成合并
        for x in equations:
            if x[1] == '=':
                index1 = ord(x[0])-ord('a')
                index2 = ord(x[3])-ord('a')
                uf.union(index1,index2)
        #判断!=表达式中两个变量是不是连通的,如果是,返回False
        for x in equations:
            if x[1] == '!':
                parent1 = uf.find(ord(x[0])-ord('a')) 
                parent2 = uf.find(ord(x[3])-ord('a')) 
                if parent1 == parent2:
                    return False
        return True           

结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值