本文代码来自于第399题力扣题解,在这里利用图解的形式再现一次解题思路以及巩固一下并查集的相关知识。
这道题目用到了并查集的相关知识,关于并查集的内容,在之前的博客中有所提及,参考并查集,你学废了吗。
回归这道题本身,变量与变量之间存在倍数关系,变量与变量之间的倍数关系具有传递性,处理有传递性关系的问题,采用并查集
的方法来进行处理,而对于变量之间的倍数关系,在并查集的合并
与查询
的操作来进行维护。
以示例1为例,说明求解过程。整个算法的过程分为三个过程:
1.初始化并查集(初始化根节点信息哈希表parents,权重信息哈希表weights)
//建立字符到其父节点的映射
unordered_map<string,string> parents;
//建立字符到其父节点的权值
unordered_map<string,double> weights;
int n=equations.size();
for(int i=0; i<n; ++i){
string a=equations[i][0];
string b=equations[i][1];
//初始化每个字符父节点为自身,权重为1.0
parents[a]=a;
parents[b]=b;
weights[a]=1.0;
weights[b]=1.0;
}
2.更新并查集(寻找根节点的过程,并不断更新)
遍历equations数组,更新并查集,即更新parents以及weights。更新的过程就是查找根节点和合并根节点的过程。下面给出函数接口,实现在查找根节点的过程中完成路径压缩,所有节点都指向根节点。
//查找每个字符的根节点,并进行路径压缩
//a->b->c
string find(string a){
//a->b parents[a]!=a
if(parents[a]!=a){
string temp=find(parents[a]);
//a->c 的权重 = a->b 的权重 * b->c 的权重
weights[a]=weights[a]*weights[parents[a]];
//a->c 连接
parents[a]=temp;
}
//返回a的父节点
return parents[a];
}
//更新并查集
for(int i=0; i<n; ++i){
string a=equations[i][0];
string b=equations[i][1];
string root_a=find(a);
// 建立 root_a -> b 的连接 并更新权重
parents[root_a]=b;
// a->root_a->b
// values[i]: a->b权重(题目给的)
// weights[a]: a->root_a 权重(已知)
// weights[root_a]: root_a->b 权重(待求)
weights[root_a]=values[i]/weights[a];
}
3.在并查集中查询queries数组中的元素
利用建立好的并查集进行最后结果的计算,应该注意结果为-1.0的情况,结合题意可以知道有三种情况,结果为-1.0
①在parents中不存在分子元素
②在parents中不存在分母元素
③分子与分母的根节点不同(这点不难理解,不具有相同的根节点,分子与分母间的倍数关系不确定)
vector<double>res;
int m=queries.size();
// 如果
// (1) 母结点哈希表不包含查询字符中的任意一个
// (2) 两个字符最终的根节点不相同
//(如 a->c b->d ab不在一个集合中)(但是 a->c->d b->d ab就可以)
// 此两种情况 都是 -1.0
for(int i=0; i<m; ++i){
string a=queries[i][0];
string b=queries[i][1];
if(!parents.count(a) || !parents.count(b) || find(a)!=find(b)){
res.push_back(-1.0);
}
else{
res.push_back(weights[a]/weights[b]);
}
}
以上就是完整的一个求解过程,下面附上完整代码:
//加权并查集
class union_find
{
private:
//建立字符到其父节点的映射
unordered_map<string,string> parents;
//建立字符到其父节点的权值
unordered_map<string,double> weights;
public:
//查找每个字符的根节点,并进行路径压缩
//a->b->c
string find(string a){
//a->b parents[a]!=a
if(parents[a]!=a){
string temp=find(parents[a]);
//a->c 的权重 = a->b 的权重 * b->c 的权重
weights[a]=weights[a]*weights[parents[a]];
//a->c 连接
parents[a]=temp;
}
//返回a的父节点
return parents[a];
}
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries){
vector<double> res;
//1.初始化并查集
int n=equations.size();
for(int i=0; i<n; ++i){
string a=equations[i][0];
string b=equations[i][1];
//初始化每个字符父节点为自身,权重为1.0
parents[a]=a;
parents[b]=b;
weights[a]=1.0;
weights[b]=1.0;
}
//2.更新并查集
for(int i=0; i<n; ++i){
string a=equations[i][0];
string b=equations[i][1];
string root_a=find(a);
// 建立 root_a -> b 的连接 并更新权重
parents[root_a]=b;
// a->root_a->b
// values[i]: a->b权重(题目给的)
// weights[a]: a->root_a 权重(已知)
// weights[root_a]: root_a->b 权重(待求)
weights[root_a]=values[i]/weights[a];
}
//3.计算最终结果
int m=queries.size();
// 如果
// (1) 母结点哈希表不包含查询字符中的任意一个
// (2) 两个字符最终的根节点不相同
//(如 a->c b->d ab不在一个集合中)(但是 a->c->d b->d ab就可以)
// 此两种情况 都是 -1.0
for(int i=0; i<m; ++i){
string a=queries[i][0];
string b=queries[i][1];
if(!parents.count(a) || !parents.count(b) || find(a)!=find(b)){
res.push_back(-1.0);
}
else{
res.push_back(weights[a]/weights[b]);
}
}
return res;
}
};
若想了解更多解法,可以前往399.除法求值查看大佬们的题解。