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 總結
直接運算划得來。遞歸竟比規劃好。奈何規劃失人心。