跳臺階問題

最近在刷一些數據結構的題,發現個很有趣的問題:跳臺階問題。

1. 第一題(引子):輸出菲波那切數列的第N項。

斐波那契數列含義(百度百科):
指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波納契數列以如下被以遞歸的方法定義:F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)

菲波那切數列是一個很有趣的數列,并且在很多的科研領域都是有應用的(就不介紹有哪些應用了,因為太(wo)高(ye)深(bu)了(hui))。

那用程序怎么實現出來呢?首先根據定義:F(n)=F(n-1)+F(n-2) 作為程序員很容易想到可以使用遞歸來實現。沒錯,下面是遞歸的實現:

    public int Fibonacci(int n) {
        if(n == 0){
            return 0;
        }else if( n<=2 ){
            return 1;
        }
        return Fibonacci(n-1)+Fibonacci(n-2);   
    }

很好理解吧,遞歸的出口就是n=0,n=1,n=2的時候

但是這種做法有什么問題呢?簡單來說這種做法的遞歸的深度是在是太深了。舉個例子:
我們計算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);
看看,多做了多少計算。2 計算了 2次,1 計算了5次,0計算了3次。正常來說我們計算4次就可以了吧。這樣相當于多做了4次。

那我們就像能不能用迭代的方式來,觀察下數列,想了下,感覺靠譜:

public static int Fibonacci(int n) {
        if(n == 0){
            return 0;
        }else if( n<=2 ){
            return 1;
        }
        int i1 = 1;
        int i2 = 1;
        int count = 3;
        while (count++<=n) {
            int number = i1+i2;
            i1 = i2;
            i2 = number;
        }
        return i2;
    }

我們知道只需要把上次的計算的值用來作為這次計算了第一項值,來循環最后就求出了我們想要的第n項的值。

看到這你可能想問了:這和跳臺階有什么關系呢?不要著急來看下這個問題:

2. 一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

想一想看一看,發現是不是有種似曾相識的感覺,可以把這個問題分解成:
如果兩種跳法,1階或者2階,那么假定第一次跳的是一階,那么剩下的是n-1個臺階,跳法是f(n-1);那么假定第一次跳的是2階,那么剩下的是n-2個臺階,跳法是f(n-2)。那總的問題是不是就變成了F(n)=F(n-1)+F(n-2)。呵呵,這不就是菲波那切數列嗎問題嗎(雖然不是完全一樣,前兩項變成了 1 2了)。代碼如下:

public static int Fibonacci(int n) {
        if(n == 0){
            return 0;
        }else if( n<=2 ){
            return 1;
        }
        int i1 = 1;
        int i2 = 1;
        //只把這里改成2就可以了,以為這個序列不是 1 1 2 3 5....了,是 1 2 3 5.... 少了個1
        int count = 2;
        while (count++<=n) {
            int number = i1+i2;
            i1 = i2;
            i2 = number;
        }
        return i2;
    }

看到這里你可能會恍然大悟,大叫原來如此。但你再看下下面這個問題:

3. 一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

如果你足夠聰明,大概已經知道套路了,我們可以看下這個問題怎么分解成一個多項式:

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

分解完了然后怎么辦呢?我們第一個想法可能是用遞歸啊,一個for循環,然后在for循環里遞歸求每個子項,然后等n為0的時候返回1就可以了。

我想說這么做也可以但是你感覺是不是很惡心,這運行起來棧的深度是不是要很深了。

那我們觀察下,看能不能化簡什么的。靈機一動,發下好像可以:
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)

完美,這回在用遞歸次數就明顯下來了。
代碼如下:
```
public int JumpFloorII(int target) {
        if (target <= 1) {
            return 1;
        } else {
            return 2 * JumpFloorII(target - 1);
        }
    }
```

做到這里,咱們是不是自然的想到,我們可以不可以算下:
####4. 一只青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個m級的臺階總共有多少種跳法。

這回我想大家應該都會了這種套路了。我們先不說話,先列多項式:
f(n) =  f(n-1) + f(n-2) + f(n-3) + ... + f(n-m)
f(n-1) =   f(n-2) + f(n-3) + ... + f(n-m) + f(n-m-1)
化簡得:f(n) = 2f(n-1) - f(n-m-1)

OK,上代碼:
```
    public static int JumpFloorIII(int target,int m ) {
        //當大于m的時候是上面的公式
        if(target > m){
            return 2*JumpFloorIII(target-1, m)-JumpFloorII(target-1-m, m);
        }
        //當小于等于m的時候就是和n級的相同了
        if (target <= 1) {
            return 1;
        } else {
            return 2 * JumpFloorIII(target - 1,target);
        }
    }
```

完美~~~
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容