本文參考自《劍指offer》一書,代碼採用Java語言。html
更多:《劍指Offer》Java實現合集 java
寫一個函數,輸入n,求斐波那契(Fibonacci)數列的第n項。git
若是直接寫遞歸函數,因爲會出現不少重複計算,效率很是底,不採用。github
要避免重複計算,採用從下往上計算,能夠把計算過了的保存起來,下次要計算時就沒必要重複計算了:先由f(0)和f(1)計算f(2),再由f(1)和f(2)計算f(3)……以此類推就好了,計算第n個時,只要保存第n-1和第n-2項就能夠了。ide
測試用例函數
1.功能測試(3,5,8等)post
2.邊界值測試(0,1,2等)性能
3.性能測試(50,100等)測試
4.特殊(負數)url
(含測試代碼)
/** * * @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開始!
時間複雜度:O(n)
斐波那契數列有如下公式(可由數學概括法推導獲得):
由上式可知,求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