题目描述
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 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] 是 '='
解题思路
本题采用并查集的思想:(不太了解并查集的小伙伴可看【算法基础】并查集的原理及使用)
- 第一遍循环,将
equations[i]
中等式都找出来,然后将等式左右两边的字母放入同一集合; - 第二遍循环,判断
equations[i]
中若有不等式两边字母属于同一集合,则返回false
,否则返回true
。
本题利用数组的方式存储每个单词的父亲节点。
用find()
函数返回该字母的根结点。
若等式两边的数字相等,则让左边数字的根结点的父亲节点指向右边数字的根结点。
在find()
函数函数中:
- 若
return find(parent[index]);
此方法为不压缩路径的方法 - 若
parent[index] = find(parent[index]);
再return parent[index];
此方法为压缩路径的方法
压缩路径的方法在查找时较快,运行所需时间较少。
具体代码
class Solution {
public:
//定义一个长度为26的数组parent,用于存放父节点
int parent[26];
//查找根结点
int find(int index){
//根结点是自身
if(index == parent[index]) return index;
//如果不是的话则一直向上寻找根结点,同时压缩路径
parent[index] = find(parent[index]);
return parent[index];
//return find(parent[index]); 此方法为不压缩路径的方法
}
//合并函数,将第一个数的根结点的父节点指向第二个数的根结点
void unite(int index1, int index2){
parent[find(index1)] = find(index2);
}
bool equationsPossible(vector<string>& equations) {
//开始时自己指向自己
for(int i = 0;i<26;i++) parent[i] = i;
//先循环一边,将相同的添加到同一集合中
for(int i = 0;i<equations.size();i++){
if(equations[i][1] == '='){
int index1 = equations[i][0] - 'a';
int index2 = equations[i][3] - 'a';
unite(index1, index2);
}
}
//开始查找并判断是否在同一集合
for(int i = 0;i<equations.size();i++){
if(equations[i][1] == '!'){
int index1 = equations[i][0] - 'a';
int index2 = equations[i][3] - 'a';
if(find(index1) == find(index2)) return false;
}
}
for(int i = 0; i<6;i++)cout<<parent[i]<<" ";
return true;
}
};