算法训练(leetcode)二刷第三十四天 | *198. 打家劫舍、*213. 打家劫舍 II、*337. 打家劫舍 III

刷题记录

  • [*198. 打家劫舍](#*198. 打家劫舍)
  • [*213. 打家劫舍 II](#*213. 打家劫舍 II)
  • [*337. 打家劫舍 III](#*337. 打家劫舍 III)

*198. 打家劫舍

leetcode题目地址

按照题目要求,不能连续选择两个相邻房屋,因此每个结点的更新有两种状态:选择当前结点或不选。

dpi存储当到第i个房屋时的最大收益。

不选:dpi = dpi-1

选:dpi = dpi-2 + numsi 因为不能连续选,因此最近只能在选i-2位置的基础上选当前结点,而dpi-2存储的是访问到第i-2个房屋时的最大收益。

和01背包有些相似。

初始化:

  • dp0初始化为nums0,代表只有一个房屋时,第一个房屋的价值就是最大收益。
  • dp1初始化为max(nums0, nums1),当有两个房屋时,选其中价值高的。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

java 复制代码
// java
class Solution {
    public int rob(int[] nums) {
        if(nums.length==1) return nums[0];
        int n = nums.length;
        int[] dp = new int[n+1];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for(int i=2; i<nums.length; i++)
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
        return dp[n-1];
    }
}

*213. 打家劫舍 II

leetcode题目地址

本题首位相接增加了难度。

可以将问题拆解为两部分:

  • 只考虑首不考虑尾
  • 只考虑尾不考虑首
    二者取最大即可。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

java 复制代码
// java
class Solution {
    public int rob(int[] nums) {
        if(nums.length==1) return nums[0];
        int n = nums.length;
        int result1 = Rangerob(nums, 0, n-2);
        int result2 = Rangerob(nums, 1, n-1);
        return Math.max(result1, result2);
    }
    public int Rangerob(int[] nums, int start, int end){
        if(end == start) return nums[start];
        int n = nums.length;
        int[] dp = new int[n];
        dp[start] = nums[start];
        dp[start+1] = Math.max(nums[start], nums[start+1]);
        for(int i=start+2; i<=end; i++){
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
        }
        return dp[end];
    }
}

*337. 打家劫舍 III

leetcode题目地址

树形动态规划,需要结合递归。

每个结点有两个状态:偷或不偷。

因此使用一个二维数组来记录结点的两个状态下的价值。

dp0表示偷当前节点可以获得的最大价值。

dp1表示不偷当前结点可以获得的最大价值。

题目要求不能连续偷两个直接相连的结点,也就是偷根节点后孩子节点就不能偷了。

借助后序遍历来进行计算,根节点基于孩子节点的结果计算,即首先需要获取左右节点偷与不偷的最大价值。

设左孩子节点的dp数组为left,右孩子结点的dp数组为right。

设cur表示根节点,若偷cur,则dp0 = cur.val + left1 + rigth1

若不偷cur,则dp1 = max(left0, left1) + max(right0, right1)

也就是说,偷了cur,就不能再偷左右孩子,而左右孩子的dp数组下标1位置记录的就是不偷该节点的最大价值;

若不偷cur,则对于左右孩子,可以偷也可以不偷,取偷与不偷二者的最大价值。

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( l o g n ) O(logn) O(logn)

java 复制代码
// java
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        int[] res = robAction(root);
        return Math.max(res[0], res[1]);
    }

    int[] robAction(TreeNode root){
        int[] res = new int[2];
        if(root == null) return res;
        int[] left = robAction(root.left);
        int[] right = robAction(root.right);
        // 偷
        res[0] = root.val + left[1] + right[1];
        // 不偷
        res[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        
        return res;
    }
}
相关推荐
随意起个昵称2 小时前
线性dp-计数类题目10(ZBRKA)
算法·动态规划
云烟成雨TD7 小时前
Spring AI Alibaba 1.x 系列【69】Token 用量统计
java·人工智能·spring
Navigator_Z7 小时前
LeetCode //C - 1089. Duplicate Zeros
c语言·算法·leetcode
JAVA9657 小时前
JAVA面试-并发篇 03-使用synchronized doublecheck实现单例有什么坑
java·单例模式·面试
在繁华处7 小时前
Java从零到熟练(四):面向对象基础
java·开发语言
小江的记录本9 小时前
【JVM虚拟机】堆内存分代模型:年轻代(Eden+Survivor)、老年代、元空间Metaspace(附《思维导图》+《面试高频考点清单》)
java·前端·jvm·后端·python·spring·面试
在繁华处9 小时前
Java从零到熟练(三):流程控制
java·开发语言·python
唐青枫9 小时前
Java Optional 实战指南:优雅处理空值与链式转换
java
一起学开源9 小时前
一文读懂 ReAct 范式:让 AI Agent 真正学会“思考+行动“
java·javascript·react.js·ecmascript·react·alibaba·智能体开发
云泽80810 小时前
C++ 可调用对象通关指南:深度解析 Lambda 表达式、function 包装器与 bind 绑定器
开发语言·c++·算法