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

这是六月八日的每日一题,可以说是并查集的具体应用吧。

题目描述

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

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

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

输入示例

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

思路

可以从离散数学角度想这个问题,先定义一个集合 A 为 26 个小写字母的集合,并定义一个 A 上的关系 R 。

先遍历一遍所有式子,如果是 == 的,就将关系加入 R 。例如遇到 a == c ,则将序偶 <a, c> 加入 R 。

遍历完毕后对 R 取等价闭包。得到所有相等的关系。

在遍历一遍所有式子,如果是 != 的,判断该关系是否在 R 中。

如果全部都不在 返回 true 否则返回 false 。

这里的等价闭包直接用并查集解决。

有关于并查集的可以查看两篇与并查集有关的文章:

代码

  • 代码:C ++

并查集实现(只使用隔代压缩):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

class UnionFind{

vector<int> parent;

public:
UnionFind(int size){
parent = vector<int>(size);
for (int i = 0 ; i < parent.size() ; i ++){
parent[i] = i;
}
}

int find(int x){
while(parent[x] != x){
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}

void union_(int x, int y){
int rootX = find(x);
int rootY = find(y);
parent[rootX] = rootY;
}

bool isConnected(int x ,int y){
int rootX = find(x);
int rootY = find(y);
return rootX == rootY;
}

};

解题代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

class Solution {
public:
bool equationsPossible(vector<string>& equations) {

auto *unionFind = new UnionFind(26);
for(auto &s:equations){
if(s[1] == '='){
unionFind->union_(s[0]-'a' ,s[3]-'a');
}
}
for(auto &s:equations){
if(s[1] == '!'){
if(unionFind->isConnected(s[0]-'a' ,s[3]-'a')){
return false;
}
}
}
return true;
}
};

结果

后记

至此,有关并查集的一切都结束。因为最近团队需要,学习了一下 Flutter ,可能会更一些有关 Flutter 的资料吧。