这个题竟然是简单??其实通过率不是很高,只有40+%
本题使用了递归或者dp,这个题深入理解可以好好体会递归和dp的区别。
题目:
两种方法,一种是递归,一种是dp。
先说递归,这里必须要记忆化递归,不然时间复杂度会超,记忆化后的时间复杂度O(n),空间复杂度O(n)。
代码:
class Solution {
private:
vector<int> m_;
int recu(vector<int> &nums, int i){
if(i < 0) return 0;
if(m_[i] >= 0) return m_[i];
m_[i] = max(recu(nums,i-2)+nums[i],recu(nums,i-1));
return m_[i];
}
public:
int rob(vector<int>& nums) {
//递归,用记忆性存储
int n=nums.size();
m_=vector<int>(n,-1);
return recu(nums,n-1);
}
};
其实很久没分析过递归了,下面是我个人对普通递归结构的理解。
- 首先,递归必须有退出条件,通常是边界条件,代码中就是 if(i < 0) return 0;
- 其次,递归必须有变化量,在这里是 i, 必须要维系其中的变化值。
- 最后,递归容易带来资源浪费,配合记忆化存储可以减少时间的浪费。
然后是使用dp的方法。
class Solution {
public:
int rob(vector<int>& nums) {
//使用dp
if(nums.empty()) return 0;
int N=nums.size();
vector<int> dp(N,0);
if(N >= 1) dp[0]=nums[0];
if(N >= 2) dp[1]=(nums[0] > nums[1]) ? nums[0] : nums[1];
for(int i=2;i<N;i++){
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[N-1];
}
};
这里面的关键就是
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
然后关键是考虑特殊情况,也就是dp0和dp1的情况。
可以看出,递归是函数反复自我调用的结果,而dp更聚焦于递推式,使用循环得出结果。
为了进一步简化写法,可以把特殊情况写到三目运算符中,下面的参考 Youtube@花花酱 但是我觉得这样有些不利于理解代码。
class Solution {
public:
int rob(vector<int>& nums) {
//使用dp
if(nums.empty()) return 0;
int N=nums.size();
vector<int> dp(N,0);
for(int i=0;i<N;i++){
dp[i]=max((i>1?dp[i-2]:0)+nums[i],
(i>0?dp[i-1]:0));//简化写法
}
return dp[N-1];
}
};