打家劫舍的题目逐层递进,对应的数据结构为数组,环形数组,二叉树:
打家劫舍I — 数组
打家劫舍II — 环形数组
打家劫舍III — 二叉树
这个题目都有一个特点,即相邻两个屋不能都被偷窃,否则会触发警报。很明显每个屋有两种状态:偷或不偷。
- 确定dp数组
dp[i] : 不偷 第 i 屋 或 偷第 i 屋 所得的最高金额 - 确定递推公式:后一个屋的结果由前两个屋得出,或父节点的结果由两个子树得出。
dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
// 偷:则 i-1 屋不能偷,从i-2屋推过来;不偷:i-1 的结果即为该屋的结果 - dp 数组初始化
dp[0] = nums[0]; // 第 0 屋肯定偷的金额最多
dp[1] = max(nums[0], nums[1]); // 第 1 屋 则比较 0 和 1 屋的金额哪个更多 - 确定遍历顺序 :显然从前往后推导
打家劫舍II — 环形数组
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
这题的房屋首尾相连,则偷了 0 屋便一定不能偷最后一个屋(n-1); 偷了 n-1 屋,便一定不能偷 0 屋。
if (nums.size() == 1)
return nums[0];
if (nums.size() == 2)
return max(nums[0], nums[1]);
// 不计nums[0],即偷 n-1 屋,相当于 0 屋不存在
vector<int>dp1(nums.size()-1, 0);
dp1[0] = nums[1];
dp1[1] = max(nums[1], nums[2]);
for (int i=3; i<nums.size(); ++i) {
dp1[i-1] = max(dp1[i-3]+nums[i], dp1[i-2]);
}
// 不计nums[n-1],即偷 0 屋,相当于 n-1 屋不存在
vector<int>dp2(nums.size()-1, 0);
dp2[0] = nums[0];
dp2[1] = max(nums[0], nums[1]);
for (int i=2; i<nums.size()-1; ++i) {
dp2[i] = max(dp2[i-2]+nums[i], dp2[i-1]);
}
return max(dp1[nums.size()-2], dp2[nums.size()-2]);