代码随想录算法训练营第34天|198.打家劫舍、213.打家劫舍II、337.打家劫舍 III
LeetCode198
2025-03-07 17:08:07 星期五
题目描述:力扣198
文档讲解:代码随想录(programmercarl)198.打家劫舍
视频讲解:《代码随想录》算法视频公开课:动态规划,偷不偷这个房间呢?| LeetCode:198.打家劫舍
记录
刚开始看这个题意,自己先写了这样的代码,把下标为0和下标为1的数,操作语句为i += 2的所有数都加了起来,之后二者比个大小。70个测试用例过了40了
class Solution {
public:
int rob(vector<int>& nums) {
int sum1 = 0, sum2 = 0;
for (int i = 1; i < nums.size(); i += 2) {
sum1 += nums[i];
}
for (int i = 0; i < nums.size(); i += 2) {
sum2 += nums[i];
}
return max(sum1, sum2);
}
};
直到后面出现了[2,1,1,2]这个用例
代码随想录视频内容简记
梳理
-
确定dp[i]数组的含义,表示考虑到下标i(包含i)的能偷的最大金币数量为dp[i]。要明确这里的dp[i]不一定就是一定要偷第i个位置,只是考虑到了第i个位置
-
确定递推公式,首先是偷第i个房间,dp[i - 2] + nums[i],如果是不偷第i个房间,那么就是dp[i - 1]即可,
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
-
初始化dp数组,
dp[0] = nums[0]
,dp[1] = max(nums[0], nums[1])
-
确定遍历顺序,直接从前向后遍历即可
-
打印dp数组
LeetCode测试
点击查看代码
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 1) return nums[0];
vector<int> dp(nums.size() + 1, 0);
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < nums.size(); i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[nums.size() - 1];
}
};
LeetCode213
题目描述:力扣213
文档讲解:代码随想录(programmercarl)213.打家劫舍II
视频讲解:《代码随想录》算法视频公开课:动态规划,房间连成环了那还偷不偷呢?| LeetCode:213.打家劫舍II
代码随想录视频内容简记
这道题和打家劫舍Ⅰ的区别就在于本题是一个环形的结构,那他的思路就是把一个环展开成一个线性结构,之后直接套用就可以了
动归五部曲和上面的一样,就是包含了几种情况
情况一,不考虑首元素和尾元素,只考虑中间元素

情况二,考虑首元素和中间元素,不考虑尾元素

情况三,考虑尾元素和中间元素,不考首元素

注意:其中情况一可以包含在任意情况二或者情况三中
LeetCode测试
这个题主要就是细节多,好多测试用例过不了。主要把size = 0,1,2三种的单独写出来,才能进行数组的截取,否则很容易出现数组越界
点击查看代码
class Solution {
private:
int robone(vector<int>& nums, int start, int end) {
int size = end - start + 1;
vector<int> dp(size + 1, 0);
dp[start] = nums[start];
dp[start + 1] = max(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[end];
}
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums[0];
if (nums.size() == 2) return max(nums[0], nums[1]);
int case1 = robone(nums, 0, nums.size() - 2);
int case2 = robone(nums, 1, nums.size() - 1);
return max(case1, case2);
}
};
LeetCode337
题目描述:力扣337
文档讲解:代码随想录(programmercarl)337.打家劫舍 III
视频讲解:《代码随想录》算法视频公开课:动态规划,房间连成树了,偷不偷呢?| LeetCode:337.打家劫舍3
代码随想录视频内容简记
本题是一道二叉树和动态规划结合的题目,树形dp。接下来还是按照二叉树递归遍历的思路进行分析,感觉还是很不好想的
梳理
-
确定函数的参数和返回值
-
确定递归的终止条件
-
确定单层递归的逻辑
大致代码内容

-
确定
vector<int> robtree(TreeNode* cur)
,这道题目返回的是一个vector数组,包括两个状态,分别是当前结点不偷所获得的最大金额,还有当前结点偷所能获得最大金额。也就是本题的dp数组就只有两个长度 -
确定递归的终止条件,
if (cur == NULL) return {0, 0};
-
确定单层递归的逻辑。首先定义
vector<int> leftdp = robtree(cur->left);vector<int> rightdp = robtree(cur->right);
-
确定dp数组的含义,定义两个val1和val2,也就是本题中的dp分别表示不偷和偷的最大金额数量
-
确定递推公式。
如果偷当前结点(比如上图的3),那么说明左右孩子都不能偷,
val1 = cur->val + leftdp[0] + rightdp[0]
。如果不偷当前结点,那么就可以从左右孩子结点的偷和不偷的状态中找到最大值,也就是
val2 = max(leftdp[0], leftdp[1]) + max(rightdp[0], rightdp[1])
最后
return {val2, val1}
即可-
dp数组初始化,已经在递归的终止条件中写好了
-
确定递归的遍历顺序,本题用的是后序遍历,需要父结点获得将左子树和右子树的最大金额进行
-
打印dp数组
-
LeetCode测试
点击查看代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
vector<int> robtree(TreeNode* cur) {
if (cur == NULL) return {0, 0};
vector<int> leftdp = robtree(cur->left);
vector<int> rightdp = robtree(cur->right);
int val1, val2;// val1表示偷,val2表示不偷
val1 = cur->val + leftdp[0] + rightdp[0];
val2 = max(leftdp[0], leftdp[1]) + max(rightdp[0], rightdp[1]);
return {val2, val1};
}
public:
int rob(TreeNode* root) {
vector<int> result = robtree(root);
return max(result[0], result[1]);
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端