Leetcode 198 打家劫舍

这个题竟然是简单??其实通过率不是很高,只有40+%

本题使用了递归或者dp,这个题深入理解可以好好体会递归和dp的区别。

题目:

两种方法,一种是递归,一种是dp。

先说递归,这里必须要记忆化递归,不然时间复杂度会超,记忆化后的时间复杂度O(n),空间复杂度O(n)。

代码:

class Solution {
private:
    vector<int> m_;
    int recu(vector<int> &amp;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>&amp; 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>&amp; 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>&amp; 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];
    }
};
原文地址:https://www.cnblogs.com/molinchn/p/13222518.html