You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?Note : Given n will be a positive integer.
本題對應(yīng)于《劍指offer》P75的跳臺階問題:
一只青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
首先我們考慮最簡單的情況。如果只有1級臺階,那顯然只有一種跳法。如果有2級臺階,那就有兩種跳法了:一種是分兩次跳,每次跳1級;另外一種就是一次跳2級。
接著我們再來討論一般的情況。把n級臺階時的跳法看成是n的函數(shù)f(n)。當n>2時,第一次跳的時候有兩種不同的選擇:
- 第一次只跳1級,此時跳上n級臺階的跳法數(shù)目等于后面剩下的n-1級臺階的跳法數(shù)目,即f(n-1)
- 第一次跳2級,此時跳上n級臺階的跳法數(shù)目等于后面剩下的n-2級臺階的跳法數(shù)目,即f(n-2)
因此n級臺階的不同跳法的總數(shù) f(n) = f(n-1) + f(n-2)
上面的分析過程,我們用到了動態(tài)規(guī)劃的方法,找到了狀態(tài)轉(zhuǎn)移方程,不難看出這實際上就是一個類斐波那契數(shù)列,只是初始條件與傳統(tǒng)的斐波那契數(shù)列略有不同,這里f(2)=2。
關(guān)于斐波那契數(shù)列,我在之前的文章中已經(jīng)詳細分析了這類問題的三種計算機解法:自上而下的遞歸實現(xiàn)太耗時,轉(zhuǎn)化為特征矩陣的乘方又太復雜,所以一般使用自底向上的迭代算法。
應(yīng)用自底向上的迭代算法求解本題的源碼如下,其時間復雜度為O(n)
public class Solution {
public int climbStairs(int n) {
if (n <= 2) {
return n;
}
int fibCurrent = 0, fibOneBack = 2, fibTwoBack = 1;
for (int i = 3; i <= n; i++) {
fibCurrent = fibOneBack + fibTwoBack;
fibTwoBack = fibOneBack;
fibOneBack = fibCurrent;
}
return fibCurrent;
}
}
系統(tǒng)準備一個函數(shù),是常數(shù)項時間復雜度比較大的事情,而且系統(tǒng)遞歸棧的大小也是有限的,所以工程上的代碼,很少使用遞歸。對于一種算法的遞歸版本,往往可以通過自己維護一個棧或者迭代的方式,將其改寫成非遞歸版本進行優(yōu)化。
《劍指offer》上還對本題進行了如下擴展
一只青蛙一次可以跳上1級臺階,也可以跳上2 級,……,也可以跳上n級,此時該青蛙跳上一個n級的臺階總共有多少種跳法?
首先我們?nèi)匀豢紤]最簡單的情況。如果只有1級臺階,那顯然只有一種跳法。如果有2級臺階,那就有兩種跳法了:一種是分兩次跳,每次跳1級;另外一種就是一次跳2級。
接著我們再來討論一般的情況。把n級臺階時的跳法看成是n的函數(shù)f(n)。當n>2時,第一次跳的時候有n種不同的選擇:
- 第一次只跳1級,此時跳上n級臺階的跳法數(shù)目等于后面剩下的n-1級臺階的跳法數(shù)目,即f(n-1);
- 第一次跳2級,此時跳上n級臺階的跳法數(shù)目等于后面剩下的n-2級臺階的跳法數(shù)目,即f(n-2);
- 第一次跳3級,此時跳上n級臺階的跳法數(shù)目等于后面剩下的n-3級臺階的跳法數(shù)目,即f(n-3);
- ......;
- 第一次跳n-1級,此時跳上n級臺階的跳法數(shù)目等于后面僅剩的1級臺階的跳法數(shù)目,即f(1);
- 從初始位置直接跳n級,這也對應(yīng)了一種跳法
綜上所述,n級臺階的不同跳法的總數(shù) f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(1) + 1
把n-1帶入上面的遞推式得 f(n-1) = f(n-2) + f(n-3) + ... + f(1) + 1
所以最終的遞推式為 f(n) = 2 * f(n-1)
應(yīng)用自底向上的迭代算法求解本題的源碼如下:
public class Solution {
public int climbStairs(int n) {
int result = 1;
if (n == 1) {
return result;
}
for (int i = 2; i <= n; i++) {
result *= 2;
}
return result;
}
}