前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LeetCode Weekly Contest 40解题思路

LeetCode Weekly Contest 40解题思路

作者头像
用户1147447
发布2019-05-26 09:24:08
4030
发布2019-05-26 09:24:08
举报
文章被收录于专栏:机器学习入门机器学习入门

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1434640

LeetCode Weekly Contest 40解题思路

详细代码可以fork下Github上leetcode项目,不定期更新。

赛题

本次周赛主要分为以下4道题:

Leetcode 637. Average of Levels in Binary Tree

Problem:

Given a non-empty binary tree, return the average value of the nodes on each level in the form of an array.

Example 1:

Input: 3 / \ 9 20 / \ 15 7 Output: 3, 14.5, 11 Explanation: The average value of nodes on level 0 is 3, on level 1 is 14.5, and on level 2 is 11. Hence return 3, 14.5, 11.

Note:

  • The range of node’s value is in the range of 32-bit signed integer.

思路:一次递归,把同一层的结点加入一个链表中,可以使用Map做映射。

代码如下:

代码语言:javascript
复制
    Map<Integer, List<Integer>> map = new HashMap<>();
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> ans = new ArrayList<>();
        if (root == null) return ans;
        dfs(root, 0);

        for (Integer key : map.keySet()){
            List<Integer> tmp = map.get(key);
            long sum = 0;
            for (int n : tmp) sum += n;
            ans.add(sum / (1.0 * tmp.size()));
        }
        return ans;
    }

    public void dfs(TreeNode root, int layer){
        if (root == null) return;
        map.computeIfAbsent(layer, k -> new ArrayList<>()).add(root.val);
        dfs(root.left, layer + 1);
        dfs(root.right, layer + 1);
    }

bfs的做法,关键:int size = queue.size();

代码如下:

代码语言:javascript
复制
    public List<Double> averageOfLevels(TreeNode root) {

        List<Double> ans = new ArrayList<>();
        if (root == null) return ans;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()){
            int size = queue.size();
            long sum = 0;
            for (int i = 0; i < size; ++i){
                TreeNode node = queue.poll();
                sum += node.val;
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
            ans.add(sum / (1.0 * size));
        }

        return ans;
    }

Leetcode 640. Solve the Equation

Problem:

Solve a given equation and return the value of x in the form of string “x=#value”. The equation contains only ‘+’, ‘-’ operation, the variable x and its coefficient. If there is no solution for the equation, return “No solution”. If there are infinite solutions for the equation, return “Infinite solutions”. If there is exactly one solution for the equation, we ensure that the value of x is an integer. Example 1: Input: “x+5-3+x=6+x-2” Output: “x=2” Example 2: Input: “x=x” Output: “Infinite solutions” Example 3: Input: “2x=x” Output: “x=0” Example 4: Input: “2x+3x-6x=x+2” Output: “x=-1” Example 5: Input: “x=x+2” Output: “No solution”

思路:区分x和数字即可,并把它们解析出来,代码如下:

代码语言:javascript
复制
    public String solveEquation(String equation) {
        String[] values = equation.split("=");
        values[0] = (values[0].charAt(0) != '-' ? "+" : "") + values[0];
        values[1] = (values[1].charAt(0) != '-' ? "+" : "") + values[1];
        values[0] = values[0].replace("+", " +").replace("-", " -");
        values[1] = values[1].replace("+", " +").replace("-", " -");

        long sumX = 0;
        long sum = 0;
        String[] nums = values[0].split("\\ ");
        for (int i = 0; i < nums.length; ++i) {
            String num = nums[i];
            if (num.isEmpty())
                continue;
            if (num.contains("x")) {
                int flag = num.substring(0, 1).equals("+") ? 1 : -1;
                sumX += (flag * Integer.parseInt(
                        (num.substring(1, num.length() - 1)).isEmpty() ? "1" : (num.substring(1, num.length() - 1))));
            } else {
                int flag = num.substring(0, 1).equals("+") ? 1 : -1;
                sum += (flag * Integer.parseInt(num.substring(1, num.length())));
            }
        }

        nums = values[1].split("\\ ");
        for (int i = 0; i < nums.length; ++i) {
            String num = nums[i];
            if (num.isEmpty())
                continue;
            if (num.contains("x")) {
                int flag = num.substring(0, 1).equals("+") ? -1 : 1;
                sumX += (flag * Integer.parseInt(
                        (num.substring(1, num.length() - 1)).isEmpty() ? "1" : (num.substring(1, num.length() - 1))));
            } else {
                int flag = num.substring(0, 1).equals("+") ? -1 : 1;
                sum += (flag * Integer.parseInt(num.substring(1, num.length())));
            }
        }

        if (sumX == 0 && sum == 0)
            return "Infinite solutions";
        if (sumX == 0 && sum != 0)
            return "No solution";
        return "x=" + (sum / (-sumX));
    }

利用正则表达式,可以简化代码,如下:

代码语言:javascript
复制
    public String solveEquation(String equation) {
        int[] res1 = parse(equation.split("=")[0]);
        int[] res2 = parse(equation.split("=")[1]);
        int x = res1[0] - res2[0];
        int n = res2[1] - res1[1];
        if (x == 0 && n == 0) return "Infinite solutions";
        if (x == 0 && n != 0) return "No solution";

        return "x=" + n / x;
    }

    public int[] parse(String express){
        String[] tokens = express.split("(?=[-+])");
        int[] res = new int[2]; 
        for (String token : tokens){
            if (token.equals("-x")) res[0] -= 1;
            else if (token.equals("+x") || token.equals("x")) res[0] += 1;
            else if (token.endsWith("x")) res[0] += Integer.parseInt(token.substring(0, token.indexOf("x")));
            else res[1] += Integer.parseInt(token);
        }
        return res;
    }

当然,我们也可以自己写解析,核心思想是对express做二次变换,以+or-为标志位,进行截断。

代码如下:

代码语言:javascript
复制
public String solveEquation(String equation) {
        String[] express = equation.split("=");
        int[] res1= parse(transform(express[0]).toCharArray());
        int[] res2 = parse(transform(express[1]).toCharArray());

        int x = res1[0] - res2[0];
        int n = res2[1] - res1[1];
        if (x == 0 && n == 0) return "Infinite solutions";
        if (x == 0 && n != 0) return "No solution";

        return "x=" + n / x;
    }

    public String transform(String express){
        return express + "+";
    }

    public int[] parse(char[] express){
        int[] res = new int[2];

        int flag = express[0] == '-' ? -1 : 1;
        int i = express[0] == '-' ? 1 : 0;
        StringBuilder sb = new StringBuilder();

        for (; i < express.length; ++i){
            if (express[i] == '-' || express[i] == '+'){
                String token = sb.toString();
                if (token.equals("x")) res[0] += (flag * 1);
                else if (token.endsWith("x")) res[0] += (flag * Integer.parseInt(token.substring(0, token.indexOf("x"))));
                else res[1] += (flag * Integer.parseInt(token));
                sb = new StringBuilder();
                flag = express[i] == '-' ? -1 : 1;
            }
            else{
                sb.append(express[i]);
            }
        }
        return res;
    }

Leetcode 638. Shopping Offers

Problem:

In LeetCode Store, there are some kinds of items to sell. Each item has a price. However, there are some special offers, and a special offer consists of one or more different kinds of items with a sale price. You are given the each item’s price, a set of special offers, and the number we need to buy for each item. The job is to output the lowest price you have to pay for exactly certain items as given, where you could make optimal use of the special offers. Each special offer is represented in the form of an array, the last number represents the price you need to pay for this special offer, other numbers represents how many specific items you could get if you buy this offer. You could use any of special offers as many times as you want.

Example 1:

Input: 2,5, [3,0,5,1,2,10], 3,2 Output: 14 Explanation:undefined There are two kinds of items, A and B. Their prices are 2and2 and 5 respectively.undefined In special offer 1, you can pay 5for3Aand0BInspecialoffer2,youcanpay5 for 3A and 0B In special offer 2, you can pay 10 for 1A and 2B.undefined You need to buy 3A and 2B, so you may pay 10 for 1A and 2B (special offer #2), and10 for 1A and 2B (special offer #2), and 4 for 2A.

Example 2:

Input: 2,3,4, [1,1,0,4,2,2,1,9], 1,2,1 Output: 11 Explanation:undefined The price of A is 2,and2, and 3 for B, 4forC.Youmaypay4 for C. You may pay 4 for 1A and 1B, and 9for2A,2Band1C.Youneedtobuy1A,2Band1C,soyoumaypay9 for 2A ,2B and 1C. You need to buy 1A ,2B and 1C, so you may pay 4 for 1A and 1B (special offer #1), and 3for1B,3 for 1B, 4 for 1C.undefined You cannot add more items, though only $9 for 2A ,2B and 1C.

Note:

  • There are at most 6 kinds of items, 100 special offers.
  • For each item, you need to buy at most 6 of them.
  • You are not allowed to buy more items than you want, even if that would lower the overall price.

思路:

刚开始想用DP去做这件事,因为special offer就那么一些,所以只要随机选择符合条件的special offer并把它们保存起来,没有什么性质可以决定它们的选取顺序,所以就遍历咯,状态是有限个,直到当前状态没有special offer符合,再用商品单价去填,比较直观的做法。我用Queue进行状态搜索,且序列化保存到map中,代码如下:

代码语言:javascript
复制
    public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
        Map<String, Integer> mem = new HashMap<>();
        mem.put(listToStr(needs), 0);

        Queue<List<Integer>> queue = new LinkedList<>();
        queue.offer(needs);
        while (!queue.isEmpty()) {
            List<Integer> aux = queue.poll();
            for (List<Integer> spec : special) {
                boolean isValid = true;
                for (int i = 0; i < aux.size(); ++i) {
                    if (spec.get(i) > aux.get(i))
                        isValid = false;
                }
                if (isValid) {
                    List<Integer> remain = new ArrayList<>();
                    for (int i = 0; i < aux.size(); ++i) {
                        remain.add(aux.get(i) - spec.get(i));
                    }
                    mem.put(listToStr(remain), Math.min(mem.get(listToStr(aux)) + spec.get(spec.size() - 1),
                            mem.containsKey(listToStr(remain)) ? mem.get(listToStr(remain)) : 1 << 30));
                    queue.offer(remain);
                }
            }
        }

        int minValue = 1 << 30;
        for (String key : mem.keySet()) {
            int[] remain = strToList(key);
            int sum = mem.get(key);
            for (int i = 0; i < remain.length; ++i) {
                sum += (remain[i] * price.get(i));
            }
            minValue = Math.min(minValue, sum);
        }
        return minValue;
    }

    private String listToStr(List<Integer> needs) {
        StringBuilder sb = new StringBuilder();
        for (Integer need : needs) {
            sb.append(need + "+");
        }
        return sb.toString().substring(0, sb.length() - 1);
    }

    private int[] strToList(String str) {
        String[] nums = str.split("\\+");
        int[] needs = new int[nums.length];
        for (int i = 0; i < nums.length; ++i) {
            needs[i] = Integer.parseInt(nums[i]);
        }
        return needs;
    }

当然,如果你能够找到该问题的子问题,那么递归的做法也是可以的,看来对note的理解是相当重要,此处递归不会stack over flow,虽然搜索的效率只有O(2n)O(2^n),但对于此题足够了。

代码如下:

代码语言:javascript
复制
public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
        dfs(price, special, needs, 0);
        return min;
    }

    int min = Integer.MAX_VALUE;
    private void dfs(List<Integer> price, List<List<Integer>> special, List<Integer> needs, int val){

        //subProbelm
        List<Integer> subPro = new ArrayList<>(needs);
        for (int j = 0; j < special.size(); ++j){
            boolean isValid = true;
            for (int k = 0; k < price.size(); ++k){
                if (special.get(j).get(k) > needs.get(k)) isValid = false;
                subPro.set(k, needs.get(k) - special.get(j).get(k));
            }
            if (isValid){
                dfs(price, special, subPro, val + special.get(j).get(price.size()));
            }
        }


        int sum = 0;
        for (int j = 0; j < price.size(); ++j){
            sum += (price.get(j) * needs.get(j));
        }
        min = Math.min(min , sum + val);
    }

Leetcode 639. Decode Ways II

Problem:

A message containing letters from A-Z is being encoded to numbers using the following mapping way: ‘A’ -> 1 ‘B’ -> 2 … ‘Z’ -> 26 Beyond that, now the encoded string can also contain the character ‘*’, which can be treated as one of the numbers from 1 to 9. Given the encoded message containing digits and the character ‘*’, return the total number of ways to decode it. Also, since the answer may be very large, you should return the output mod 109 + 7.

Example 1:

Input: “*” Output: 9 Explanation: The encoded message can be decoded to the string: “A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I”.

Example 2:

Input: “1*” Output: 9 + 9 = 18

Note:

  • The length of the input string will fit in range 1, 105.
  • The input string will only contain the character ‘*’ and digits ‘0’ - ‘9’.

思路:

重点描述从递归到迭代的一个过程,这个问题是一个基本的归简递归转DP题,思路是比较简单的,但细节比较复杂。

首先,对于*218231**02*1这样的一串数字,该怎么寻找子问题?这里只会出现两种模式:

代码语言:javascript
复制
模式1:(*),子问题A:(218231**02*1)
模式2:(*2),子问题B:(18231**02*1)

且很容易理解,如果能够求出子问题的解,那么由模式1得到(1~9) * 子问题A的解,即 9 * subPro(218231**02*1),同理由模式2得,ans = 2 * subPro(18231**02*1) ,这样该题的答案就全部出来了。至于为什么相乘,是排列组合问题,分别有9种和2种情况罢了,非常直观。

上述思路可以用递归去求解,但递归的本质在于把所有的子问题化归到一个较小的集合中求解,比如上述化归一定到个数为1的字符串,如{*} = 9, {1} = 1,既然如此,我们可以优先计算最右字符串,从长度为1的子问题开始,再由这些子问题生成进一步地解,这就是动规。根据题目能确定一些基本生成规则,具体可以参考:https://leetcode.com/problems/decode-ways-ii/#/description

代码如下:

代码语言:javascript
复制
    static final int MOD = 1000000000 + 7;
    public int numDecodings(String s) {
        char[] c = s.toCharArray();
        int n = c.length;
        long[] dp = new long[n + 16];
        dp[n] = 1;
        if (c[n - 1] == '*') dp[n - 1] = 9;
        else if (c[n - 1] == '0') dp[n - 1] = 0;
        else dp[n - 1] = 1;

        for (int i = n - 2; i >= 0; --i) {
            if (c[i] == '0') {
                dp[i] = 0;
            } else if (c[i] == '*') {
                dp[i] = (dp[i] + 9 * dp[i + 1]) % MOD;
                if (c[i + 1] == '*') {
                    dp[i] = (dp[i] + 15 * dp[i + 2]) % MOD;
                } else if (c[i + 1] - '0' <= 6) {
                    dp[i] = (dp[i] + 2 * dp[i + 2]) % MOD;
                } else {
                    dp[i] = (dp[i] + dp[i + 2]) % MOD;
                }
            } else {
                dp[i] = (dp[i] + dp[i + 1]) % MOD;
                if (c[i] == '1') {
                    if (c[i + 1] == '*') {
                        dp[i] = (dp[i] + 9 * dp[i + 2]) % MOD;
                    } else {
                        dp[i] = (dp[i] + dp[i + 2]) % MOD;
                    }
                } else if (c[i] == '2') {
                    if (c[i + 1] == '*') {
                        dp[i] = (dp[i] + 6 * dp[i + 2]) % MOD;
                    } else if (c[i + 1] - '0' <= 6) {
                        dp[i] = (dp[i] + dp[i + 2]) % MOD;
                    }
                }
            }
        }
        return (int) dp[0];
    }

子问题可以用一个游标的状态来记录。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年07月17日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • LeetCode Weekly Contest 40解题思路
    • 赛题
      • Leetcode 637. Average of Levels in Binary Tree
        • Leetcode 640. Solve the Equation
          • Leetcode 638. Shopping Offers
            • Leetcode 639. Decode Ways II
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档