198.打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
1. dp[i]:考虑下标为i以内的房屋最多能够盗窃的金额是多少;
2. 如果偷第i个房间,那么dp[i] = dp[i-2]+nums[i],而如果不偷第i个房间,那么dp[i]=dp[i-1],即考虑i-1号房。于是dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
3. dp数组如何初始化:根据递推公式,dp[i]的值应该由dp[0], dp[1]的值决定。dp[0]的值是nums[0]而dp[1]的值是max(nums[0], nums[1])。
4. 遍历顺序:从前往后遍历
class Solution {
public:
int rob(vector<int>& nums) {
int s = nums.size();
if (s == 0) return 0;
if (s == 1) return nums[0];
vector<int> dp(s+5, 0);
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for(int i=2; i<s; i++){
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[s-1];
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)
213.打家劫舍II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
对于一个数组,成环的话主要有如下三种情况:
- 情况一:考虑不包含首尾元素
- 情况二:考虑包含首元素,不包含尾元素
- 情况三:考虑包含尾元素,不包含首元素
跟上一道题类似,只是这一次需要对比情况2和3的大小:
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
int result1 = robrange(nums, 0, nums.size()-2);
int result2 = robrange(nums, 1, nums.size()-1);
return max(result1, result2);
}
int robrange(vector<int>& nums, int start, int end){
if(end==start) return nums[start];
vector<int> dp(nums.size(), 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-2]+nums[i], dp[i-1]);
}
return dp[end];
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(n)
337.打家劫舍 III
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
本题是对二叉树的遍历,本题一定是要后序遍历,因为通过递归函数的返回值来做下一步计算。
当使用暴力解法时,代码如下:
class Solution {
public:
unordered_map<TreeNode*, int> umap;
int rob(TreeNode* root) {
if(root == NULL) return 0;
if(root->left==NULL && root->right==NULL) return root->val;
if(umap[root]) return umap[root];
int val1 = root->val;
if(root->left) val1 += rob(root->left->left) + rob(root->left->right);
if(root->right) val1 += rob(root->right->left) + rob(root->right->right);
//不偷父结点
int val2 = rob(root->left) + rob(root->right);
umap[root] = max(val1, val2);
return max(val1, val2);
}
};
- 时间复杂度:O(n),每个节点只遍历了一次
- 空间复杂度:O(log n),算上递推系统栈的空间
我们计算了root的四个孙子(左右孩子的孩子)为头结点的子树的情况,又计算了root的左右孩子为头结点的子树的情况,计算左右孩子的时候其实又把孙子计算了一遍。
所以我们可以使用一个map将计算过的结果保存一下。