Leetcode 399. 除法求值(Java实现 超详细注释!)

Leetcode 399. 除法求值

图+深度优先遍历,哇,好难!自己根据理解加了详细的注释,方便日后复习,也希望能帮到其他小伙伴,如有错误,欢迎指正!

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

你可能感兴趣的:(Leetcode,dfs)