力扣 990. 等式方程的可满足性

题目说明

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

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

  • 提示:
    • 1 <= equations.length <= 500
    • equations[i].length == 4
    • equations[i][0]equations[i][3] 是小写字母
    • equations[i][1] 要么是 =,要么是 !
    • equations[i][2]=

示例

示例 1:

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

示例 2:

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

示例 3:

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

示例 4:

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

示例 5:

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

笔者理解

此题是一道并查集算法问题,在力扣题库中被定义为中等题。

解法

当笔者阅读完此题后,发现此题需要用到并查集这种数据结构,并查集顾名思义,就是带有合并和查询的一种集合,他的结构类似于一个公司内上下级的关系,让我们来看看具体如何实现的吧。

实现

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class Solution {
public boolean equationsPossible(String[] equations) {
int n = equations.length;

// 并查集数组,长度为26是因为题中规定了只存在小写字母
DisjointSet[] dict = new DisjointSet[26];

// 初始化并查集节点,并将上级设定为自己
for (int i = 0; i < 26; i++) {
dict[i] = new DisjointSet((char) ('a' + i));
dict[i].parent = dict[i];
}

// 先将各个等式整理好,也就是将各字母的上下级关系整理好
// 存在上下级关系代表着这两个字母的值相等
for (int i = 0; i < n; i++) {
if (equations[i].charAt(1) == '=') {

// 如果两个字母已经相等,就不进行连接操作了
if (dict[equations[i].charAt(0) - 'a'].find()
== dict[equations[i].charAt(3) - 'a'].find()) {
continue;
}
dict[equations[i].charAt(0) - 'a'].union(dict[equations[i].charAt(3) - 'a']);
}
}

// 再来看不等式,因为不相等关系和相等关系相悖,所以只需要遍历所有的不等式
// 看是否有违背上下级关系的关系即可
for (int i = 0; i < n; i++) {
if (equations[i].charAt(1) == '!') {
if (dict[equations[i].charAt(0) - 'a'].find() == dict[equations[i].charAt(3) - 'a'].find()) {
return false;
}
}
}

return true;
}

class DisjointSet {
// 当前节点的值
char data;

// 当前节点的最上级节点
// 初始时为自己
DisjointSet parent;

DisjointSet() {}

DisjointSet(char data) {
this.data = data;
}

// 并查集节点的连接
public void union(DisjointSet other) {
DisjointSet temp = this.parent;

// 找到当前节点的最上级节点
while (temp.parent.data != temp.data) {
temp = temp.parent;
}

// 将最上级节点的上级设为给定节点
temp.parent = other;
}

// 寻找当前节点的最上级
public char find() {
DisjointSet temp = this.parent;
while (temp.parent.data != temp.data) {
temp = temp.parent;
}
return temp.data;
}
}
}

时间和空间效率都还行,可见此解法还比较适合此题;

image-20210523160717027

总结

本题是今天的一题,难度是为中等,感兴趣的朋友都可以去尝试一下,此题还有其他更多的解法,朋友们可以自己逐一尝试。