本文参考自《剑指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