• 打家劫舍系列问题


    打家劫舍系列问题

    作者:Grey

    原文地址:

    博客园:打家劫舍系列问题

    CSDN: 打家劫舍系列问题

    LeetCode 198. 打家劫舍

    主要思路

    定义和原始数组一样长的dp数组,

    int[] dp = new int[dp]
    

    dp[i]的含义是:[0...i]区间内,得到最大的金额是多少。

    显然有

    // [0...0]范围内,最大金额就是`arr[0]`
    dp[0] = arr[0];
    // [0...1]范围内,最大金额要不选`arr[0]`,要不选`arr[1]`,不能同时选,因为会报警
    dp[1] = Math.max(arr[0],arr[1]);
    

    针对普遍位置dp[i],有两个方案:

    方案1,不选择i位置,这样的话dp[i] = dp[i-1],即:i位置的答案,只依赖i-1位置的答案。

    方案2,选择i位置,这样的话dp[i] = arr[i] + dp[i - 2],即:i位置的答案,依赖i位置的值加上i-2位置的答案。

    以上两种方案取最大值,就是i当前位置的最大获取金额数。

    dp[i] = Math.max(dp[i - 1], arr[i] + dp[i - 2]);
    

    完整代码

        public static int rob(int[] arr) {
            if (arr == null || arr.length == 0) {
                return 0;
            }
            if (arr.length == 1) {
                return arr[0];
            }
            if (arr.length == 2) {
                return Math.max(arr[0], arr[1]);
            }
            final int n = arr.length;
            int[] dp = new int[n];
            dp[0] = arr[0];
            dp[1] = Math.max(arr[0], arr[1]);
            for (int i = 2; i < n; i++) {
                dp[i] = Math.max(dp[i - 1], arr[i] + dp[i - 2]);
            }
            return dp[n - 1];
        }
    

    时间复杂度O(N),空间复杂度O(N),根据如上代码,可以看到,整个dp数组是有递推关系的,所以,可以进行压缩数组优化,仅需要几个变量就可以实现上述过程,优化后的代码如下

        public static int rob2(int[] arr) {
            if (arr == null || arr.length == 0) {
                return 0;
            }
            if (arr.length == 1) {
                return arr[0];
            }
            if (arr.length == 2) {
                return Math.max(arr[0], arr[1]);
            }
            final int n = arr.length;
            int prePre = arr[0];
            int pre = Math.max(arr[0], arr[1]);
            int max = pre;
            for (int i = 2; i < n; i++) {
                int cur = Math.max(pre, prePre + arr[i]);
                prePre = pre;
                pre = cur;
                max = Math.max(cur, max);
            }
            return max;
        }
    

    以上优化后的算法,时间复杂度O(N),空间复杂度O(1)

    LeetCode 213. 打家劫舍 II

    主要思路

    之前的问题不是环形数组,本题是环形数组,所以,最后一个位置和第一个位置有强关联关系,所以我们分两部分来解

    第一部分,不包括最后一个位置。

    第二部分,一定要包括最后一个位置。

    上述两个部分的最大值,就是整体的要求的答案。

    第一部分,不包括最后一个位置,那么整个区间就是下标从[0....n-2],调用上一个非环形数组问题的解法。

            final int n = arr.length;
            // 以下情况是考虑最后一个位置
            int prePre = arr[1];
            int pre = Math.max(arr[1], arr[2]);
            int max = pre;
            for (int i = 3; i < n; i++) {
                int cur = Math.max(pre, prePre + arr[i]);
                prePre = pre;
                pre = cur;
                max = Math.max(cur, max);
            }
    

    第二部分,包括最后一个位置,那么整个区间就是下标从[1...n-1],调用上一个非环形数组问题的解法。

            // 以下情况是不考虑最后一个位置
            prePre = arr[0];
            pre = Math.max(arr[0], arr[1]);
            for (int i = 2; i < n - 1; i++) {
                int cur = Math.max(pre, prePre + arr[i]);
                prePre = pre;
                pre = cur;
                max = Math.max(cur, max);
            }
    

    最后max即为环形数组下的答案。完整代码见

        public static int rob(int[] arr) {
            if (arr == null || arr.length == 0) {
                return 0;
            }
            if (arr.length == 1) {
                return arr[0];
            }
            if (arr.length == 2) {
                return Math.max(arr[0], arr[1]);
            }
            if (arr.length == 3) {
                return Math.max(Math.max(arr[0], arr[1]), arr[2]);
            }
            final int n = arr.length;
            // 以下情况是考虑最后一个位置
            int prePre = arr[1];
            int pre = Math.max(arr[1], arr[2]);
            int max = pre;
            for (int i = 3; i < n; i++) {
                int cur = Math.max(pre, prePre + arr[i]);
                prePre = pre;
                pre = cur;
                max = Math.max(cur, max);
            }
            // 以下情况是不考虑最后一个位置
            prePre = arr[0];
            pre = Math.max(arr[0], arr[1]);
            for (int i = 2; i < n - 1; i++) {
                int cur = Math.max(pre, prePre + arr[i]);
                prePre = pre;
                pre = cur;
                max = Math.max(cur, max);
            }
            return max;
        }
    

    LeetCode 337. 打家劫舍 III

    主要思路

    由于变成了树状,所以,对于任意子树,只需要考虑两种情况即可:

    情况1,包含子树头节点的情况下,最大收益是多少。

    情况2,不包含子树头节点的情况下,最大收益是多少。

    定义数据结构

        public class Info {
            // 选头节点
            public int yes;
            // 不选头节点
            public int no;
    
            public Info(int y, int n) {
                yes = y;
                no = n;
            }
        }
    

    同时定义递归函数

    public Info p(TreeNode root) {
        // TODO
    }
    

    这个递归函数的含义是:以root为头节点的树的最大收益是多少。可能性有如下几种

    可能性1,最大值包含了root节点,那么对于root的左子树left和右子树right,需要获取到不包含其左右子树头节点的情况下,最大收益是多少。

     int yes = root.val + left.no + right.no;
    

    可能性2,最大值不包含root节点,那么对于root的最有子树,只要给出左右子树获取到的最大收益就可以了,不需要考虑左右子树头节点是否包含进来的问题。

    int no = Math.max(left.yes, left.no) + Math.max(right.yes, right.no);
    

    最后Math.max(yes,no)就是以root为头的数得到的最大收益是多少。

    完整代码如下

        public int rob(TreeNode root) {
            Info info = p(root);
            return Math.max(info.yes, info.no);
        }
    
        public class Info {
            // 选头节点
            public int yes;
            // 不选头节点
            public int no;
    
            public Info(int y, int n) {
                yes = y;
                no = n;
            }
        }
    
        public Info p(TreeNode root) {
            if (root == null) {
                return new Info(0, 0);
            }
            Info left = p(root.left);
            Info right = p(root.right);
            int yes = root.val + left.no + right.no;
            int no = Math.max(left.yes, left.no) + Math.max(right.yes, right.no);
            return new Info(yes, no);
        }
    

    更多

    算法和数据结构笔记

  • 相关阅读:
    java中用spring实现数组类型输出
    #科委外文文献发现系统——导出word模板1.0
    #科委外文文献发现系统——项目管理
    终审项目展示
    M2事后会议报告
    【Beta版本发布】爬虫队长装备全面更新!
    Beta版本测试报告
    Beta阶段爬取数目预估
    团队作业Week14——源代码管理
    Daily Scrum NO.10
  • 原文地址:https://www.cnblogs.com/greyzeng/p/16494637.html
Copyright © 2020-2023  润新知