剑指 Offer 10- I. 斐波那契数列、II. 青蛙跳台阶问题


        

这两个都是同一种类型的问题:即斐波那契动态规划问题。

一. 对第一题进行分析

  对于这种问题,我们首先得出动态规划的转移方程为:f[n] = f[n-1] + f[n-2],其中 f[0] = 0,f[1] = 1。但是如果我们就这样算,肯定会超出时间限制,因此我们可以用一个数组 dp 来记录算出的斐波那契数列。dp[i] = dp[i-1] + dp[i-2]。这样我们可以得出 dp[3]、dp[4]……这样在我们使用他们的时候,就不用重复计算了,最后返回 dp[n] 即可(根据题目要求,dp[0] = 0不计入项数)。

  但是我们发现,题目中只需要返回第n项斐波那契常数,不需要记录前面的,因此我们可以简化空间复杂度。定义三个数 p,q=0,r=1(q和r 也就是斐波那契数列的第0项和第1项),进行for循环滑动

class Solution {
    public final int mod = 1000000007;
    public int fib(int n) {
        if(n<2){
            //n<2直接输出
            return n;
        }else{
            int p,q=0,r=1;
            for(int i=2;i<=n;i++){
                //滑动
                p = q;
                q = r;
                r = (p+q)%mod; //避免溢出
            }
            return r;
        }
    }
}

我们还需要注意:不是在for循环结束后 让 r %= mod,而是在for循环里就不断求余(这两者是等价的),因为不这样做的话,可能在for循环里 r就已经溢出了。 

二. 对第二题进行分析

  第二题表面上看起来和斐波那契数列没有什么关系,但我们通过动态规划可以发现:对于最后一个台阶,要么是倒数第二个台阶跳一步上来的,要么是倒数第三个台阶跳两步上来的,因此得到动态规划的转移方程为 dp[i] = dp[i-1] + dp[i-2](跳到倒数第二个台阶上的方法 + 跳到倒数第三个台阶上的方法)。

  这题与斐波那契不同的地方在于,题目规定 dp[0] = 1,dp[1] = 1。根据降低空间复杂度的方法:我们定义 p,q=1,r=1。

class Solution {
    public int mod = 1000000007;
    public int numWays(int n) {
        if(n==0||n==1){
            return 1;
        }
        int p, q=1,r=1;
        for(int i=2;i<=n;i++){
            p = q;
            q = r;
            r = p + q;
            //不这样有可能越界出现负号
            r%=mod;
        }
        return r;
    }
}