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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Java中的排序功能来实现。可以使用Arrays.sort()函数,将列表中的元素按照字母顺序排序,或者使用Collections.sort()函数,将列表中的元素按用户指定的排序规则排序。 ### 回答2: 为了实现LeetCode 2561题(Rearranging Fruits)的要求,需要使用Java编程语言。主要思路是遍历给定的水果数组,同时用一个哈希表来记录每个水果出现的次数。然后根据题目要求,重新排列水果使得相同类型的水果尽可能接近,并且按照出现次数的非递增顺序排序。 具体实现步骤如下: 1. 创建一个HashMap来存储每个水果的出现次数。遍历给定的水果数组,如果该水果已经存在于HashMap中,则将其出现次数加1;否则,将该水果添加到HashMap,并将其出现次数初始化为1。 2. 创建一个ArrayList来存储已经排列好的水果。通过HashMap的entrySet方法获取到每种水果和它的出现次数,然后将这些entry按照出现次数的非递增顺序进行排序。 3. 遍历排序好的entry集合,根据每个水果的出现次数,在ArrayList中连续添加相应数量的水果。 4. 返回排列好的水果数组。 以下是Java代码的示例实现: ```java import java.util.*; class Solution { public String[] rearrange(String[] fruits) { HashMap<String, Integer> fruitCountMap = new HashMap<>(); // 统计每个水果的出现次数 for (String fruit : fruits) { if (fruitCountMap.containsKey(fruit)) { fruitCountMap.put(fruit, fruitCountMap.get(fruit) + 1); } else { fruitCountMap.put(fruit, 1); } } ArrayList<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(fruitCountMap.entrySet()); // 根据出现次数进行非递增排序 Collections.sort(sortedEntries, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> entry1, Map.Entry<String, Integer> entry2) { return entry2.getValue().compareTo(entry1.getValue()); } }); ArrayList<String> rearrangedFruits = new ArrayList<>(); // 根据出现次数连续添加水果 for (Map.Entry<String, Integer> entry : sortedEntries) { String fruit = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { rearrangedFruits.add(fruit); } } return rearrangedFruits.toArray(new String[0]); } } ``` 使用以上代码,可以对给定的水果数组进行重新排列,使得相同类型的水果尽可能接近,并且按照出现次数的非递增顺序进行排序。返回的结果就是排列好的水果数组。 ### 回答3: 题目要求将一个字符串中的水果按照特定规则重新排列。我们可以使用Java实现这个问题。 首先,我们需要定义一个函数来解决这个问题。 ```java public static String rearrangeFruits(String fruits) { // 将字符串转换为字符数组方便处理 char[] fruitArray = fruits.toCharArray(); // 统计每种水果的数量 int[] fruitCount = new int[26]; for (char fruit : fruitArray) { fruitCount[fruit - 'a']++; } // 创建一个新的字符数组来存储重新排列后的结果 char[] rearrangedFruitArray = new char[fruitArray.length]; // 逐个将水果按照规则放入新数组中 int index = 0; for (int i = 0; i < 26; i++) { while (fruitCount[i] > 0) { rearrangedFruitArray[index++] = (char) ('a' + i); fruitCount[i]--; } } // 将字符数组转换为字符串并返回 return new String(rearrangedFruitArray); } ``` 上述代码中,我们首先将字符串转换为字符数组,并使用一个长度为26的数组来统计每一种水果的数量。然后,我们创建一个新的字符数组来存储重新排列后的结果。 接下来,我们利用双重循环将每一种水果按照规则放入新数组中。最后,我们将字符数组转换为字符串并返回。 例如,如果输入字符串为`"acbba"`,则经过重新排列后,输出结果为`"aabbc"`。 这样,我们就用Java实现了题目要求的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值