【剑指offer解题笔记】斐波那契数列与跳台阶问题与矩形覆盖问题

目录

斐波那契数列

思路分析:

 

跳台阶问题

思路分析:

 

变态跳台阶

思路分析:

矩形覆盖问题

思路分析:

 


斐波那契数列

现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。

思路分析:

斐波那契数列,1,1,2,3,5,8.....,从第三项开始,每一项的值是前两项之和。

可以得出:f(n) = f(n-1) + f(n-2),第一眼看就是递归啊,简直完美的递归环境,递归肯定很爽,这样想着关键代码两三行就搞定了,注意这题的n是从0开始的:

if(n<=1) return n;
else return Fibonacci(n-1)+Fibonacci(n-2);

然而并没有什么用,测试用例里肯定准备着一个超大的n来让Stack Overflow,为什么会溢出?因为重复计算,而且重复的情况还很严重,举个小点的例子,n=4,看看程序怎么跑的:

Fibonacci(4) = Fibonacci(3) + Fibonacci(2);

                    = Fibonacci(2) + Fibonacci(1) + Fibonacci(1) + Fibonacci(0);

                    = Fibonacci(1) + Fibonacci(0) + Fibonacci(1) + Fibonacci(1) + Fibonacci(0);

由于我们的代码并没有记录Fibonacci(1)和Fibonacci(0)的结果,对于程序来说它每次递归都是未知的,因此光是n=4时f(1)就重复计算了3次之多,并且速度也非常慢。牛客网上的编辑器中运行时间达到652ms。

【剑指offer解题笔记】斐波那契数列与跳台阶问题与矩形覆盖问题_第1张图片

所以推荐的解法并不是递归,而是迭代。

从题目中可以发现,对下一个值n有影响的仅仅是前面的两个值(n-1)和(n-2)而已,所以我们可以使用两个变量来循环存储n前面的两个值。上代码:

public class Solution {
    public int Fibonacci(int n) {
        int a=1;  //定义a存储第一个值
        int b=1;  //定义b存储第二个值
        if(n<=0){     //<=0的情况不在我们计算范围内,直接返回
            return 0;
        }else if(n>0&&n<3){   //斐波那契数列前两位都是1,所以直接返回1
            return 1;
        }else{   //其余的输入情况都在>=3的范围,可以计算。
        for(int i=3;i<=n;i++){   //斐波那契数列,从第三项开始迭代,所以是i=3
            int temp=b;   //定义一个临时变量保存b,否则b会丢掉
            b=b+a;  //前两项相加
            a=temp; //由于新的b已经产生,作为第二项,所以将之前的保存b的临时变量赋给a,产生新的第一项
        }
            }
        return b; 
    }
}

 代码正确运行无误,运行时间缩短至23ms。

【剑指offer解题笔记】斐波那契数列与跳台阶问题与矩形覆盖问题_第2张图片

 


 

跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

 

思路分析:

总跳法数量f(n)。

有两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法数量是f(n-1)。

假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法数量是f(n-2)。

所以得出总跳法为: f(n) = f(n-1) + f(n-2) 

然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2

可以发现最终得出的是一个斐波那契数列:

        

              1, (n=1)

f(n) =     2, (n=2)

              f(n-1)+f(n-2) ,(n>2,n为整数)

所以除了部分用例不一样,从3开始的结果和之前我们写的斐波那契数列是一样的哦,那么上代码。

public class Solution {
    public int JumpFloor(int target) {
        
        int a=1;
        int b=2;
        
        if(target<=0){
            return 0;
        }
        else if(target==1){
            return 1;
        }
        else if(target==2){   //仅仅是部分用例发生了改变
            return 2;
        }
        else{
            for(int n=3;n<=target;n++){
            int temp=b;
            b=a+b;
            a=temp;
         
                }
               return b;
        }

    }
}

 


 

变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

 

思路分析:

f(1) = 1

f(2) = f(2-1) + f(2-2)         //f(2-2) 表示2阶一次跳2阶的次数。

f(3) = f(3-1) + f(3-2) + f(3-3) 

...

f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n) 

说明: 

1)这里的f(n) 代表的是n个台阶有一次1,2,...n阶的 跳法数。

2)n = 1时,只有1种跳法,f(1) = 1

3) n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2) 

4) n = 3时,会有三种跳得方式,1阶、2阶、3阶,

    那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)

    因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)

5) n = n时,会有n中跳的方式,1阶、2阶...n阶,得出结论:

    f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)

    

6) 由以上已经是一种结论,但是为了简单,我们可以继续简化:

    f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1)

    = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)

    f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1)

    = f(n-1) + f(n-1)

    可以得出:

    f(n) = 2*f(n-1)

    

7) 得出最终结论,在n阶台阶,一次有1、2、...n阶的跳的方式时,总得跳法为:

              | 1       ,(n=0 ) 

f(n) =     | 1       ,(n=1 )

              | 2*f(n-1),(n>=2)

 

代码如下。

public class Solution {
    public int SuperJumpFloor(int target) {
        if (target <= 0) {
            return -1;
        } else if (target == 1) {
            return 1;
        } else {
            return 2 * SuperJumpFloor(target - 1);
        }
    }
}

矩形覆盖问题

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

 

思路分析:

2*n的大矩形,和n个2*1的小矩形,其中target*2为大矩阵的大小,有以下几种情形:

target <= 0 大矩形为<= 2*0,直接return 0;

target = 1大矩形为2*1,只有一种摆放方法,return1;

target = 2 大矩形为2*2,有两种摆放方法,return2;

target = n 分为两步考虑:

【剑指offer解题笔记】斐波那契数列与跳台阶问题与矩形覆盖问题_第3张图片

看到这里,同学们应该发现,这不就是跳台阶问题的变形么?同样是分析第一步的两种不同选择,然后将两种不同选择的结果相加即可得到总结果,依旧是斐波那契数列。

代码:

public class Solution {
    public int RectCover(int target) {
        int a=1;
        int b=2;
        if(target<=0){
            return 0;
        }
        else if(target==1){
            return 1;
        }
        else if(target==2){
            return 2;
        }else{
            for(int n=3;n<=target;n++){
            int temp=b;
            b=a+b;
            a=temp;
         
                }
               return b;
        }
    }
}

 

你可能感兴趣的:(剑指offer)