細說劍指offer(2)_變態跳臺階之不變態

1. 題外話

1.1 什麼叫遞推式

如果一個規律(數學題、編程題),可以利用遞推的思想去推導出一個公式來解決問題的話。那麼這個公式就叫做遞推式。

2. 題目描述

2.1 變態跳臺階

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

3.解法1——遞歸

3.1 代碼

public int JumpFloorII(int target) {
        if (target < 2)
            return target;
        
        return JumpFloorII( target - 1) * 2; //推到遞推式求出
    }

3.2 分析

代碼解讀:

  • 此種方法求出此題的遞推式是關鍵,設有以下兩個式子。
  • 式子1:f(n) = f(n - 1) + f(n - 2) + ~~~ + f(0)
  • 式子2:f( n - 1) = f(n - 2) + f( n - 2) + ~~~ + f(0)
  • 式子1 - 式子2 得到本題的遞推式——f(n) = 2*f(n - 1)
  • 當然f(0) = 0,f(1) = 1
  • 得到遞推式後,我們可以選擇利用遞歸的方法求出解。

複雜度解讀:

  • 雖然遞歸的次數不多,但是本身遞歸方法入棧出棧(線程的方法棧)也會消耗時間空間。勉強算是O(n)。

4. 解法2——算術解法

4.1 代碼

public int JumpFloorII(int target) {
        return (int)Math.pow(2, target - 1); //直接2的target - 1次方
    }

4.2 分析

代碼解讀:

  • 驚訝?no,因爲如果我們仔細算f(n)的話,會發現0,1,2,4,8,16尼瑪不就是2的次方嘛!當然f(n) = 2*f(n - 1)也提醒你了。

複雜度解讀:

  • 運算級別,時間O(1),不過可以再改進下。因爲是2的倍數的運算,所以我們可以採用位運算。改爲 return 1<<(target - 1);
  • 啥?不知道位運算?建議好好複習下計算機基礎。

5. 解法3——動態規劃

5.1 代碼

public int JumpFloorII(int target) {
        if (target < 2)//不用算啦,直接返回
            return target;
        
        int[] dp = new int[target + 1];// 你想返回dp[target],你確定不多加一格?
        
        dp[0] = 0;
        
        for(int i = 0; i <= target; i++){
            for(int j = i - 1; j > 0; j--){
                dp[i] += dp[j];//dp[n]的跳法等於dp[n - 1] + dp[n - 2] + ~~~+ dp[0]種跳法
            }
            dp[i]++;//本身就有直接跳到n來,所以+1
        }
        
        return dp[target];
    }

5.2 分析

代碼解讀:

  • 動態規劃的思想。臺階爲n的解法,是臺階爲n-1一直到0的臺階解法的和。所以代碼中有兩層循環。

複雜度解讀:

  • O(n的平方),很慢的一種算法。且有額外的空間消耗。

6 總結

直接運算划得來。遞歸竟比規劃好。奈何規劃失人心。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章