《剑指Offer》学习笔记--面试题9:斐波那契数列
2015-05-06 17:05
555 查看
题目一:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。斐波那契数列数列的定义如下:
公式f
=f[n-1]+f[n-2],且f[1]=1,f[2]=1。
效率很低的递归算法,面试官都不会喜欢
面试官期待的使用解法
其实改进的方法并不复杂。上述递归代码之所以慢是因为重复计算太多,我们只要想办法避免重复计算就行了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过就不用再重复计算了。
更简单的方法是从下网上算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3).......以此类推就可以算出第n项了。很容易理解,这种思路的时间复杂度是O(n)。
实现代码如下:
矩阵快速幂求Fibonacci。
代码如下:
用不同的方法求解斐波那契数列的时间效率大不相同。第一种基于递归的解法虽然直观但是时间效率很低,在实际软件开发中不会用这种方法,也不可能得到面试官的青睐。第二种方法把递归的算法用循环实现,极大地提高了时间效率。第三种方法把斐波那契数列转换成求矩阵的乘方,是一种很有创意的算法。虽然我们可以用O(logn)求得矩阵的n次方,但由于隐含的时间常数较大,很少会有软件采用这种算法。另外实现这种解法的代码也很复杂,不太适用面试。因此第三种方法不是一种实用的算法,不过应聘者可以用它来展现自己的知识面。
除了面试官直接要求编程实现斐波那契数列之外,还有不少面试题可以看成是斐波那契数列的应用,比如:
题目二:一只青蛙可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级台阶总共有多少种跳法。
首先我们考虑最简单的情况。如果只有一级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳法了:一种是分两次跳,没跳1级;另外一种就是一次跳2级。
接着我们再来讨论一般情况。我们把n级台阶时的跳法看成时n的函数,记为f(n)。当n>2时,第一次跳的时候就有两种不同的选择:
(1)第一次只挑1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);
(2)选择第一次跳两级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。
因此n级台阶的不同跳法的总数f(n) = f(n-1) + f(n-2)。分析到这里我们不难看出这实际上就是斐波那契数列了。
类似的题目还有骨牌覆盖问题,参见我的另一篇博文:算法-骨牌覆盖问题(矩阵快速幂求Fibonacii)
公式f
=f[n-1]+f[n-2],且f[1]=1,f[2]=1。
效率很低的递归算法,面试官都不会喜欢
long long Fibonacci(unsigned int n) { if(n <= 0){ return 0; } if(n == 1){ return 1; } return Fibonacci(n-1) + Fibonacci(n-2); }我们的教科书上反复用这个问题来讲解递归函数,并不能说明递归的解法最适合这道题目。
面试官期待的使用解法
其实改进的方法并不复杂。上述递归代码之所以慢是因为重复计算太多,我们只要想办法避免重复计算就行了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过就不用再重复计算了。
更简单的方法是从下网上算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3).......以此类推就可以算出第n项了。很容易理解,这种思路的时间复杂度是O(n)。
实现代码如下:
#include <iostream> using namespace std; long long Fibonacci(unsigned n) { int result[2] = {0,1}; if(n < 2) return result ; long long fibNMinusOne = 1; long long fibNMinusTwo = 0; long long fibN = 0; for(unsigned int i = 2; i <= n; i++){ fibN = fibNMinusOne + fibNMinusTwo; fibNMinusTwo = fibNMinusOne; fibNMinusOne = fibN; } return fibN; } int main() { int n; while(cin>>n){ cout<<Fibonacci(n)<<endl; } system("pause"); return 0; }时间复杂度O(logn)但不够实用的解法
矩阵快速幂求Fibonacci。
代码如下:
#include <iostream> using namespace std; const int MOD = 19999997; struct matrix{ //重载结构体 public: long long a; long long b; long long c; long long d; matrix &operator * (matrix &data){ //重载* long long tempa = a; long long tempb = b; long long tempc = c; long long tempd = d; a = tempa*data.a%MOD+tempb*data.c%MOD; b = tempa*data.b%MOD+tempb*data.d%MOD; c = tempc*data.a%MOD+tempd*data.c%MOD; d = tempc*data.b%MOD+tempd*data.d%MOD; return *this; } matrix &operator = (matrix &data){ //重载& a = data.a; b = data.b; c = data.c; d = data.d; return *this; } }; long long fastFibonacci(long n) { matrix res = {1,1,1,0}; matrix base = {1,1,1,0}; matrix temp; n++; while(n){ if(n&1){ res = res*base; } temp = base; base = base*temp; n >>=1; } return res.d; } int main() { long n; while(cin>>n){ cout<<fastFibonacci(n)%MOD<<endl; } system("pause"); return 0; }解法比较
用不同的方法求解斐波那契数列的时间效率大不相同。第一种基于递归的解法虽然直观但是时间效率很低,在实际软件开发中不会用这种方法,也不可能得到面试官的青睐。第二种方法把递归的算法用循环实现,极大地提高了时间效率。第三种方法把斐波那契数列转换成求矩阵的乘方,是一种很有创意的算法。虽然我们可以用O(logn)求得矩阵的n次方,但由于隐含的时间常数较大,很少会有软件采用这种算法。另外实现这种解法的代码也很复杂,不太适用面试。因此第三种方法不是一种实用的算法,不过应聘者可以用它来展现自己的知识面。
除了面试官直接要求编程实现斐波那契数列之外,还有不少面试题可以看成是斐波那契数列的应用,比如:
题目二:一只青蛙可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级台阶总共有多少种跳法。
首先我们考虑最简单的情况。如果只有一级台阶,那显然只有一种跳法。如果有2级台阶,那就有两种跳法了:一种是分两次跳,没跳1级;另外一种就是一次跳2级。
接着我们再来讨论一般情况。我们把n级台阶时的跳法看成时n的函数,记为f(n)。当n>2时,第一次跳的时候就有两种不同的选择:
(1)第一次只挑1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,即为f(n-1);
(2)选择第一次跳两级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,即为f(n-2)。
因此n级台阶的不同跳法的总数f(n) = f(n-1) + f(n-2)。分析到这里我们不难看出这实际上就是斐波那契数列了。
类似的题目还有骨牌覆盖问题,参见我的另一篇博文:算法-骨牌覆盖问题(矩阵快速幂求Fibonacii)
相关文章推荐
- 《剑指Offer》学习笔记--面试题20:顺时针打印矩阵
- 《剑指Offer》学习笔记--面试题29:数组中出现次数超过一半的数字
- 《剑指Offer》学习笔记--面试题39:二叉树的深度
- 《剑指Offer》学习笔记--面试题49:把字符串转换成整数
- 《剑指Offer》学习笔记--面试题56:链表中环的入口结点
- 《剑指Offer》学习笔记--面试题61:按之字形顺序打印二叉树
- 《剑指Offer》学习笔记--面试题12:打印1到最大的n位数
- 《剑指Offer》学习笔记--面试题50:树中两个结点的最低公共祖先
- 《剑指Offer》学习笔记--面试题52:构建乘积数组
- 《剑指Offer》学习笔记--面试题3:二维数组中的查找
- 《剑指Offer》学习笔记--面试题10:二进制中1的个数
- 《剑指Offer》学习笔记--面试题21:包含min函数的栈
- 《剑指Offer》学习笔记--面试题34:丑数
- 《剑指Offer》学习笔记--面试题40:数组中只出现一次的数字
- 《剑指Offer》学习笔记--面试题44:扑克牌的顺子
- 《剑指Offer》学习笔记--面试题57:删除链表中重复的结点
- 《剑指Offer》学习笔记--面试题18:树的子结构
- 剑指Offer学习之面试题9 : 斐波那契数列
- 《剑指Offer》学习笔记--面试题54:表示数值的字符串
- 《剑指Offer》学习笔记--面试题43:n个骰子的点数