【Java】 劍指offer(9) 斐波那契數列及青蛙跳台階問題


 本文參考自《劍指offer》一書,代碼采用Java語言。

更多:《劍指Offer》Java實現合集  

題目

  寫一個函數,輸入n,求斐波那契(Fibonacci)數列的第n項。

思路

  如果直接寫遞歸函數,由於會出現很多重復計算,效率非常底,不采用。

  要避免重復計算,采用從下往上計算,可以把計算過了的保存起來,下次要計算時就不必重復計算了:先由f(0)和f(1)計算f(2),再由f(1)和f(2)計算f(3)……以此類推就行了,計算第n個時,只要保存第n-1和第n-2項就可以了。

測試用例

  1.功能測試(3,5,8等)

  2.邊界值測試(0,1,2等)

  3.性能測試(50,100等)

  4.特殊(負數)

完整Java代碼

(含測試代碼)

/**
 * 
 * @Description 斐波那契數列
 *
 * @author yongh
 * @date 2018年9月13日 下午7:19:36
 */

// 題目:寫一個函數,輸入n,求斐波那契(Fibonacci)數列的第n項。

public class Fibonacci {
	public long Fib(long n) {
		if(n<0)
			throw new RuntimeException("下標錯誤,應從0開始!");
		if (n == 0)
			return 0;
		if (n == 1)
			return 1;
		long prePre = 0;
		long pre = 1;
		long result = 1;
		for (long i = 2; i <= n; i++) {
			result = prePre + pre;
			prePre = pre;
			pre = result;
		}
		return result;
	}
	
	//附:縮略版(考慮到代碼的可讀性,其實還是上面的方法比較好)
	public long Fib2(long n) {
		if(n<0)
			throw new RuntimeException("下標錯誤,應從0開始!");
		if (n == 0)
			return 0;
		if (n == 1)
			return 1;
		long pre = 0;
		long result = 1;
		for (long i = 2; i <= n; i++) {
			result += pre;
			pre = result - pre;
		}
		return result;
	}

	public static void main(String[] args) {
		Fibonacci demo = new Fibonacci();
		System.out.println(demo.Fib(0));
		System.out.println(demo.Fib(1));
		System.out.println(demo.Fib(2));
		System.out.println(demo.Fib(8));
		System.out.println(demo.Fib(50));
		System.out.println(demo.Fib(100));
		System.out.println(demo.Fib(-5));
	}
}

  

0
1
1
21
12586269025
3736710778780434371
Exception in thread "main" java.lang.RuntimeException: 下標錯誤,應從0開始!
Fibonacci

   時間復雜度:O(n)

拓展

時間復雜度為O(longn)的解法

  斐波那契數列有以下公式(可由數學歸納法推導得到):

  

  由上式可知,求f(n),只需要對矩陣求(n-1)次方即可,但此時時間復雜度仍為O(n)。利用乘方的性質

  利用遞歸的思路計算乘方,即可將時間復雜度降低為O(longn)。這里給出對乘方函數的遞歸代碼(引用):

Matrix2By2 MatrixPower(unsigned int n)
{
    assert(n > 0);

    Matrix2By2 matrix;
    if(n == 1)
    {
        matrix = Matrix2By2(1, 1, 1, 0);
    }
    else if(n % 2 == 0)
    {
        matrix = MatrixPower(n / 2);
        matrix = MatrixMultiply(matrix, matrix);
    }
    else if(n % 2 == 1)
    {
        matrix = MatrixPower((n - 1) / 2);
        matrix = MatrixMultiply(matrix, matrix);
        matrix = MatrixMultiply(matrix, Matrix2By2(1, 1, 1, 0));
    }

    return matrix;
}

  

青蛙跳台階問題

 題目1:一只青蛙一次可以跳上1級台階,也可以跳上2級。求該青蛙跳上一個n級的台階總共有多少種跳法。

  將跳法總數記為f(n),可以知道f(1)=1,f(2)=2。當n>2時,第一次跳1級的話,還有f(n-1)種跳法;第一次跳2級的話,還有f(n-2)種跳法,所以可以推得f(n)=f(n-1)+f(n-2),即為斐波那契數列

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

  解法1:

  當n=1時,f(1)=1。

  當n大於1時,歸納總結可知:跳上n級台階,第一次跳1級的話,有f(n-1)種方法;第一次跳2級的話,有f(n-2)種方法……第一次跳n-1級的話,有f(1)種方法;直接跳n級的話,有1種方法,所以可以得到如下公式:

  f(n) = f(n-1)+f(n-2)+......f(1)+1  (n≥2)

  f(n-1) = f(n-2)+f(n-3)+.....f(1)+1  (n>2)

  由上面兩式相減可得,f(n)-f(n-1)=f(n-1),即f(n) = 2*f(n-1)  (n>2)

  最終結合f(1)和f(2),可以推得:f(n)=2^(n-1)

  解法2:

  假設跳到第n級總共需要k次,說明要在中間n-1級台階中選出任意k-1個台階,即C(n-1,k-1)種方法。

  所以:跳1次就跳上n級台階,需要C(n-1,0)種方法;跳2次需要C(n-1,1)種方法……跳n次需要C(n-1,n-1)種方法

  總共需要跳C(n-1,0)+C(n-1,1)+C(n-1,2)+……C(n-1,n-1)=2^(n-1)種方法。

  解法3:

  除了必須到達最后一級台階,第1級到第n-1級台階都可以有選擇的跳,也就是說對於這n-1個台階來說,每個台階都有跳上和不跳上2種情況,所以一共有2^(n-1)種方法。

 

矩形覆蓋問題

 題目:用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

  當n = 1時,有一種方法。

  當n = 2時,有兩種方法。

  當n >= 3時,和斐波那契數列類似。第一步豎着放,有f(n-1)種方法;第一步橫着放,有f(n-2)種方法。所以f(n)=f(n-1)+f(n-2)。

收獲

  1.求n次方時,可以利用遞歸來降低時間復雜度

  2.當遇到涉及n的問題時(類似青蛙跳台階問題),不要緊張,可以進行歸納分析,特別注意f(n)與f(n-1)、f(n-2)等的關聯,從而找出規律,進行合理建模。

  3.return (int)Math.pow(2,target-1);

    1) 轉int類型

    2)pow不是power

 

更多:《劍指Offer》Java實現合集  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM