青蛙跳臺階相關問題

想了解更多數據結構以及算法題,可以關注微信公衆號“數據結構和算法”,每天一題爲你精彩解答。也可以掃描下面的二維碼關注
在這裏插入圖片描述

問題一:
一隻青蛙一次可以跳上一級臺階,也可以跳上二級臺階,求該青蛙跳上一個n級的臺階總共需要多少種跳法。

我們來分析一下:

當n等於1的時候,只需要跳一次即可,只有一種跳法,記f(1)=1

當n等於2的時候,可以先跳一級再跳一級,或者直接跳二級,共有2種跳法,記f(2)=2

當n等於3的時候,他可以從一級臺階上跳兩步上來,也可以從二級臺階上跳一步上來,所以總共有f(3)=f(2)+f(1);

同理當等於n的時候,總共有f(n)=f(n-1)+f(n-2)(這裏n>2)種跳法。
在這裏插入圖片描述
所以大家一看就知道這就是個斐波那契數列,只不過有一點不同的是斐波那契數列一般是以1,1,2,3,5,8,13……開始的,而我們這是以1,2,3,5,8,13……開始的,少了最前面的一個1。最代碼很簡單

1	public static int f(int n) {
2    if (n < 3)
3          return n;
4	    return f(n - 1) + f(n - 2);
5	}

我們以計算f(6)爲例畫個圖看一下計算的過程
在這裏插入圖片描述
我們看到遞歸會重複計算已經計算過的值,效率明顯不是很高,我們還可以把計算過的值儲存起來,防止重複計算,我們來看下代碼

 1	private static int f2(int n, HashMap<Integer, Integer> map) {
 2	    if (n < 3) return n;
 3	    if (map.containsKey(n))
 4 	       return map.get(n);
 5 	   int first = f2(n - 1, map);
 6 	   int second = f2(n - 2, map);
 7 	   int sum = first + second;
 8	    map.put(n, sum);
 9 	   return sum;
10	}

我們還可以把遞歸改爲非遞歸的形式,看下代碼

 1	private static int f3(int n) {
 2	    if (n < 3)
 3	        return n;
 4	    int first = 1, second = 2, sum = 0;
 5 	    while (n-- > 2) {
 6 	        sum = first + second;
 7 	        first = second;
 8	        second = sum;
 9     }
10	    return sum;
11	}

上面3種方式都可以實現青蛙跳臺階問題,那麼哪種效率更高呢,我們來找個比較大的數據測試一下

 1	public static void main(String[] args) {
 2 	   int step = 45;
 3     long time = System.nanoTime();
 4 	   System.out.println(f(step));
 5     System.out.println("代碼優化前時間:" + (System.nanoTime() - time));
 6     time = System.nanoTime();
 7     System.out.println(f2(step, new HashMap<Integer, Integer>()));
 8 	   System.out.println("代碼優化後時間:" + (System.nanoTime() - time));
 9     time = System.nanoTime();
10 	   System.out.println(f3(step));
11 	   System.out.println("代碼非遞歸時間:" + (System.nanoTime() - time));
12	}

來看一下運行的時間

1	1836311903
2	代碼優化前時間:2221741900
3	1836311903
4	代碼優化後時間:108000
5	1836311903
6	代碼非遞歸時間:17600

我們看到遞歸優化之前運行時間是非常長的,優化之後時間大幅下降,但對於非遞歸來說又稍遜色了一些。

問題二:
一隻青蛙一次可以跳上一級臺階,也可以跳上二級臺階……,也可以跳n級,求該青蛙跳上一個n級的臺階總共需要多少種跳法。

我們來分析一下
一隻青蛙要想跳到n級臺階,可以從一級,二級……,也就是說可以從任何一級跳到n級,
在這裏插入圖片描述
所以遞推公式我們很容易就能想到
f(n)=f(n-1)+f(n-2)+……+f(2)+f(1)+f(0);最後這個f(0)是可以去掉的,因爲0級就相當於沒跳,所以f(0)=0;
然後我們把f(0)去掉在轉換一下:
f(n-1)=f(n-2)+f(n-3)+……+f(2)+f(1);
所以f(n)=f(n-1)+f(n-1)=2*f(n-1);他是一個等比數列,且f(1)=1;
我們我們可以得出f(n)=2^(n-1);代碼如下

1	private static int f4(int n) {
2	    if (n == 1)
3 	       return 1;
4 	   return f4(n - 1) * 2;
5	}

或者還可以改爲非遞歸的

1	private static int f5(int n) {
2 	   if (n == 1)
3  	      return 1;
4 	   return 1 << (n - 1);
5	}

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

這道題我們要分開討論:
1,如果n<=m;因爲只能往上跳不能往下跳,所以大於n的都不可以跳,如果跳了就直接超過了,只能跳小於等於n的數字,那麼這個問題就直接退到問題2了。

2,如果n>m;我們要想跳到n級臺階,我們可以從n-1級跳一步上來,或者從n-2級跳兩步上來……,或者從n-m級跳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);(n>m)
所以代碼我們要分爲兩部分,一部分是n>m,另一部分是n<=m,我們來看下代碼

1	public static int f6(int n, int m) {
2	    if (n <= 1)
3 	       return 1;
4  	  //總檯階大於跳的最高級臺階
5	    if (n > m)
6	        return 2 * f6(n - 1, m) - f6(n - 1 - m, m);
7  	  //回退到上面的問題二了
8 	   return 2 * f6(n - 1, n);
9	}

斐波那契數列又稱黃金分割數列,他有很多的特性,比如兔子的繁殖,他的通項公式如下
在這裏插入圖片描述

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