代码随想录算法训练营day53 || 198. 打家劫舍,213.打家劫舍II,337.打家劫舍 III

本文详细讨论了如何使用动态规划解决LeetCode中的打家劫舍系列问题,包括基本版本、环形数组和树形结构。重点介绍了状态转移和空间时间复杂度的优化方法。
摘要由CSDN通过智能技术生成

视频讲解:

动态规划,偷不偷这个房间呢?| LeetCode:198.打家劫舍_哔哩哔哩_bilibili

动态规划,房间连成环了那还偷不偷呢?| LeetCode:213.打家劫舍II_哔哩哔哩_bilibili

动态规划,房间连成树了,偷不偷呢?| LeetCode:337.打家劫舍3_哔哩哔哩_bilibili

198. 打家劫舍

思路:其实和爬楼梯的题目非常的类似,但是缺少了每次可以爬一级的思想,因此状态转移的过程就是从2开始,统计dp[j-2……k]+nums[j] 这样统计。自己想了一种方法,是采用间隔遍历的方式,每个位置都统计之前的所有位置所传递而来的最大值,但是所形成的结果就是完全区别了dp[n]和dp[n-1]两个位置的结果,所以最后两者需要比较一下输出最大值。

// 时间复杂度O(n^2)
// 空间复杂度O(n)

// class Solution {
//     public int rob(int[] nums) {
//         // 初步感觉,类似于爬楼梯,但是每次爬一级楼梯不行
//         if(nums.length == 1)
//             return nums[0];
//         if(nums.length == 2)
//             return Math.max(nums[0], nums[1]);

//         int[] dp = new int[nums.length+1];
//         // 房屋的金额视作为物品
//         // 第几个房屋视作为重量
//         dp[0] = 0;
//         dp[1] = nums[0];
//         dp[2] = nums[1];
//         for(int i=3; i<=nums.length; i++){
//             for(int j=2; i-j>=0; j++){
//                 dp[i] = Math.max(dp[i], dp[i-j]+nums[i-1]);
//             }
//         }

//         return Math.max(dp[nums.length], dp[nums.length-1]);
//     }
// }


// 时间复杂度O(n)
// 空间复杂度O(n)
class Solution {
	public int rob(int[] nums) {
		if (nums == null || nums.length == 0) return 0;
		if (nums.length == 1) return nums[0];

        // dp表示的是到第 i+1 户人家可以偷取的最大金额
        // 这种dp策略是涉及每个位置的最大值,是相邻位置也需要进行考虑,而我上面的那种dp其实是间隔的位置的最大值
		int[] dp = new int[nums.length];
        // 初始化
		dp[0] = nums[0];
		dp[1] = Math.max(dp[0], nums[1]);
		for (int i = 2; i < nums.length; i++) {
            // dp[i - 1]表示当前i位置不偷,偷i-1位置所取得的金额,与当前偷了i位置取得的金额取较大的一方
			dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
		}

        // 那么我们可以对打家劫舍问题作出一点心得:
        // 1、在i位置,所考虑的来源仅仅是i-1;另外状态之间的转换上,i位置始终依赖的就是i-2的,关于这一点,因为我们遍历顺序是从头开始,且dp内总是存储的最优值,因此在后续遍历i时,只需要考虑可行的i-2,与先前的最优化进一步关联
        // 2、递推公式的核心就是偷i与不偷i所取得的收益进行比较求最优,甚至本质思想是依赖贪心来进行的
		return dp[nums.length - 1];
	}
}

213.打家劫舍II

思路:本题相较于 打家劫舍I 所区别在于数组是循环的,即i位置往后遍历与 打家劫舍I 遍历的方式一致,但是i-1 位置不可以再访问。递推公式与 打家劫舍I 的公式一致,而遍历顺序上,可从数组的任一位置开始,因此我们可以选择从数组的0位置开始,并且注意特殊的nums.length-1位置是从0位置开始遍历时不可以再接触的一个位置。

其实归根到底这么想,是因为打家劫舍系列的题目,i位置的状态迁移总是与i-1和i-2两个位置相关,i继承于i-2,i-1是i所参考对比的,那么在访问过程中,以及最后输出结果的时候,需要判别的就是dp[n] dp[n-1]两个位置;同样对于本题循环,设置两个方向,从0和nums.length-1处开始遍历即可求解,这和 打家劫舍I 中第一种解法的想法是贯通的。

// 时间复杂度O(n)
// 空间复杂度O(2n)

class Solution {
    public int rob(int[] nums) {

        if(nums.length == 1)
            return nums[0];
        if(nums.length == 2)
            return Math.max(nums[0], nums[1]);
        if(nums.length == 3)
            return Math.max(nums[0], Math.max(nums[1], nums[2]));

        int n = nums.length;
        int[] dp = new int[nums.length];
        int[] DP = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for(int i=2; i<nums.length-1; i++)
            dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
        
        DP[0] = nums[n-1];
        DP[1] = Math.max(nums[n-1], nums[0]);
        for(int i=2; i<nums.length-1; i++){
            int index = (n-1+i)%n;
            DP[i] = Math.max(DP[i-1], DP[i-2]+nums[index]);
        }

        return Math.max(dp[n-2], DP[n-2]);
    }
}

337.打家劫舍 III

思路:树形结构的动态规划题目,本题不会,做不出来。题解先理解背诵。树形结构的题目,确定先序/中序/后序的遍历顺序非常的重要

/**
 * 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;
 *     }
 * }
 */

// 时间复杂度O(n),n为节点的个数
// 空间复杂度O(2n)

class Solution {
    public int rob(TreeNode root) {
        int[] res = robAction(root);
        return Math.max(res[0], res[1]);
    }

    public int[] robAction(TreeNode root){
        if(root == null)
            return new int[]{0,0};
        int[] res = new int[2];
        // 后序遍历操作,优先完成左右孩子金额的计算,用来配合父节点呃金额的计算,并且父节点的金额确实依赖子节点
        int[] left = robAction(root.left);
        int[] right = robAction(root.right);

        res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        res[1] = root.val + left[0] + right[0];
        return res;


    }
}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值