题目
这道题要是不知道并查集的方法还可以通过DFS或BFS来做,这也是我第一次在leetcode遇到图相关的题目,很有意义!事实上题中的相除关系可以通过图的方式来表达,而queries中的求商可以通过求换算关系来得到,比如求a/d,那么我们要是能够将a和d都换算成相同的中间变量即可!
我们可将a/b = 2.0变为两个顶点和一个边的关系来表示,a—>b,权重为2; 那么再加上b/c = 3.0,将图扩展为a—>b—>c,权重分别为2.0,3.0。那么当我们要求a/c的值的时候,只需要求a到c的值即可!
那么我们可以应用并查集的方法了:并查集由两步组成,分别是合并union和查找find,合并是将并不联通的两个图通过代表元进行联通,那么联通后的两个图就成为一个图,有同样的代表元了!而查找就是通过一个顶点查找该顶点所在图中的代表元!在本题中合并可以用于建立equations各个变量的联通关系(除法关系),在同一联通图的变量(顶点)可以换算成同一另一元素(代表元),查找用于找一个变量可以换算到另外一个变量(代表元),同时添加方法计算从被查找顶点到代表元的权值乘积!
在一个讲解并查集的博客中看到一个很有意思的理解方式,我们可以将合并操作理解为帮派的合并,而代表元就是一个帮派的老大,那么帮派的合并自然要是A帮的帮主投靠B帮的B帮主或是B帮的B帮主A帮的帮主了!而查找操作可以理解为底下的小弟去找老大!并查集还有Rank优化,路径压缩等优化操作,在此不累书。
class Solution {
public:
unordered_map<string, string> parents;
unordered_map<string, double> weights;
// 查找
string Find(string s, double& results){
string s_root = s;
while(parents.count(s_root)>0){
results*=weights[s_root];
s_root = parents[s_root];
}
return s_root;
}
//合并
void Union(string x, string y, double x_y){
double x_results = 1;
double y_results = 1;
string x_root = Find(x,x_results);
string y_root = Find(y,y_results);
if(x_root == y_root)
return;
parents[x_root] = y_root;
if(weights.count(y) == 0)
weights[y] = 1.0;
if(weights.count(x) == 0)
weights[x] = 1.0;
if(weights.count(y_root) == 0)
weights[y_root] = 1.0;
weights[x_root] = x_y*y_results/x_results;
}
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
for(int i=0;i<equations.size();i++){
string x = equations[i][0];
string y = equations[i][1];
double x_y = values[i];
Union(x,y,x_y);
}
vector<double> res;
for(int i=0;i<queries.size();i++){
string x = queries[i][0];
string y = queries[i][1];
double x_result = 1;
double y_result = 1;
string x_root = Find(x,x_result);
string y_root = Find(y,y_result);
if(weights.count(x) == 0 || weights.count(y)==0)
res.push_back(-1.0);
else if(x_root == y_root)
res.push_back(x_result/y_result);
else
res.push_back(-1.0);
}
return res;
}
};