图+深度优先遍历,哇,好难!自己根据理解加了详细的注释,方便日后复习,也希望能帮到其他小伙伴,如有错误,欢迎指正!
Java实现:
class Solution {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
// 图的数据结构
Map<String,Map<String,Double>> graph = new HashMap<>();
// 我们构建这个图
setGraph(graph,equations,values);
// 初始化res,同样是一一对应,每一个queries的元素对应结果res中的一个元素,因此res数组的长度和queries是一致的
double[] res = new double[queries.size()];
Arrays.fill(res,-1.0);
// 遍历queries
int index = 0;
for (List<String> que : queries){
// 取到一个queries元素中的一对问题
String c = que.get(0);
String d = que.get(1);
// 如果该问题中的两个元素有一个不存在,那答案肯定是算不出来的
if (!graph.containsKey(c) || !graph.containsKey(d)){
}else{
// 如果该问题的两个元素在图中都存在,我们就需要去寻找这个问题的解了,我们这里可以用深度优先遍历
dfs(graph,c,d,res,index,new HashSet<>(),1.0);
}
index ++;
}
return res;
}
// 深度优先遍历每个题目,直到找到答案或者找不到答案(其实就是看c和d是不是连通的)
// 我们要输入的参数有:1)图 2、3)问题的两个元素 4)res,方便在dfs中直接赋值,否则传回来比较麻烦 5)index 索引,不传索引,数组没法赋值
// 6)visited也要传,否则每次dfs都重新初始化visited,就达不到效果了 7)tmp 便于累乘
private void dfs(Map<String,Map<String,Double>> graph,String c,String d,double[] res,int index ,Set<String> visited,double tmp){
visited.add(c);
// 首先看看图中有没有c这个根节点,或者c下面没有可连通的元素,直接返回-1.0即可,一开始res已经fill了,这里我们直接返回即可
if (graph.get(c) == null || graph.get(c).size() == 0){
return;
}
// 如果有c存储,且c下面直接连着d,那么说明c和d是直接相连的,我们直接赋值graph.get(c).get(d)即可
// 但是下面需要深度优先遍历,需要不断累乘,因此这里需要乘上一轮的tmp,但是由于这这种tmp = 1.0,所以这里乘tmp不影响这种情况且能兼顾dfs
if (graph.get(c).containsKey(d)){
// graph.get(a).get(b)就相当取到a/b的值
res[index] = graph.get(c).get(d) * tmp;
return;
}
// 如果不是上面两种情况,即c和d存在,但是d不是和c直接相连的,我们需要去深度优先遍历c下面的所有元素。看看能否找到d
for (String nex : graph.get(c).keySet()){
// 由于元素是可能自己和自己相连的,如果我们不记录已经访问过的元素并跳过,这里会栈内存溢出
if (visited.contains(nex)) continue;
// 使用递归进行深度优先遍历,最后一个参数是这一轮的权重乘以上上轮的权重,不能肯定两次dfs就一定能找到d,还可能继续dfs,不能漏了*tmp
dfs(graph,nex,d,res,index,visited, graph.get(c).get(nex) * tmp);
}
}
private void setGraph(Map<String,Map<String,Double>> graph ,List<List<String>> equations,double[] values){
// 处理每一个变量数组对
int i = 0;
for (List<String> arr : equations){
String a = arr.get(0);
String b = arr.get(1);
// 存入根节点,putIfAbsent表示如果重复不进行替换
graph.putIfAbsent(a,new HashMap<>());
graph.putIfAbsent(b,new HashMap<>());
// 将对应的值存入map,现在graph中的一个桶内疚有一个这样的数据:{a:{b:values[i]}} ,这表示a/b = values[i]
graph.get(a).put(b,values[i]);
// 同时因为我们构造的是一个有向图,我们把反向的也存一下
graph.get(b).put(a,1.0/values[i]);
i++; // 因为变量对和实数值必须一一对应,我们需要将实数值的指针向后移动
// 同时,我们自己是可以除自己的,我们添加一下这种情况
graph.get(a).put(a,1.0);
graph.get(b).put(b,1.0);
// 到此我们将所有的数组对都遍历结束后,我们就构造了整个有向图
}
}
}
本文算法参考视频:
https://www.bilibili.com/video/BV1z4411K7ns