題目:
有一座高度是10級臺階的樓梯,從下往上走,每跨一步只能向上1級或者2級臺階,要求用程序來求出一共有多少種走法?
例子,每次走1級臺階,一共走10級臺階,這是其中一種走法,我們可以簡寫成 1,1,1,1,1,1,1,1,1,1
解題思路:
假設你只差最後一步走到10級臺階,這時候會出現幾種情況?
這時只會出現兩種情況,
- 第一種:最後一步走1級臺階,第一種情況對應的是已經走到了第9級臺階
- 第二種:最後一步走2級臺階,第二種情況對應的是已經走到了第8級臺階
此時並不具體考慮如何走到第9或者第8級臺階的
假設走到第10級臺階的走法有F(10)
則走到第9級臺階的走法有F(9)
則走到第8級臺階的走法有F(8)
則F(10)=F(9)+F(8)
由題意可知
F(1)=1
F(2)=2
解法一:常規的迭代算法
public static int getClimbingWays(int n) {
if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return getClimbingWays(n - 1) + getClimbingWays(n - 2);
}
存在的問題:
此計算過程類似一顆二叉樹,頂點是F(10),左右子節點是F(9)和F(8),依次計算下去,這個二叉樹層高10-1,節點個數2^10-1,
時間複雜度近似O(2^n)
這一顆計算二叉樹中有許多重複的計算部分,
這些重複的部分事實上我們可以採用緩存將其保存起來
解法二:採用緩存的方式,以降低時間複雜度
具體實現如下:
public static int getClimbingWaysCache(int n, HashMap<Integer, Integer> map) {
if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
if (map.containsKey(n)) {
return map.get(n);
} else {
Integer value = getClimbingWays(n - 1) + getClimbingWays(n - 2);
map.put(n, value);
return value;
}
}
在以上代碼中,集合map是一個緩存。當每次需要計算F(N)的時候,會首先從map中尋找匹配元素。如果map中存在,就直接返回結果,如果map中不存在,就計算出結果,存入緩存中。
此時時間複雜度:O(n),需要計算F(10),F(9),。。。這些數字
此時空間複雜度:O(n),需要保存F(10),F(9),.。。。F(3),由於F(1)和F(2)都是已知的不需要緩存在備忘錄中
問題
這種方法中所需要的空間複雜度爲O(n),這種計算方法採用的自頂向下的計算方式,所以必須保存每次計算的結果,以便後期取用。如何降低空間複雜度?
解法三:採用自底向上算法,以降低空間複雜度
實現
public static int getClimbingCacheCacheImp(int n) {
if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
int a = 1;
int b = 2;
int temp = 0;
for (int i = 3; i <= n; i++) {
temp = a+b;
a = b;
b = temp;
}
return temp;
}
程序從 i=3 開始迭代,一直到 i=n 結束。每一次迭代,都會計算出多一級臺階的走法數量。迭代過程中只需保留兩個臨時變量a和b,分別代表了上一次和上上次迭代的結果,最後的結果由temp保存。
此時時間複雜度:O(n),需要計算F(10),F(9),。。。這些數字
此時空間複雜度:O(1),需要保存a,b,temp,所需空間爲3(常量)