题目一:写一个函数,输入n,求斐波那契数列的第n项。斐波那契数列的定义如下:
1、效率很低效的解法,挑剔的面试官不会喜欢
很多C语言的教科书在讲述递归函数的时候,都户拿Fibonacci作为例子,因此很多的应聘者对这道题的递归解法都很熟悉。
下面是实现代码
我们教科书上反复用这个问题来讲解递归的函数,并不能说明递归的解法最适合这道题目。面试官会提示我们上述递归的解法有很严重的效率问题要求我们分析原因。
我们以求解f(10)为例来分析递归的求解过程。想求得f(10),需要先求出f(9)和f(8).同样求f(9),需要先求得f(8)和f(7)。我们用树来构造这种依赖关系。如图所示:
我们不难发现在这颗树中有很多的节点是重复的,而且重复的节点数会随着n的增大而急剧增加,这意味着计算量会随着n的增大而急剧增大。事实上,用递归的方法计算的时间复杂度是以n的指数的方式递增的。读者不妨求Fibonacci的第100项试试,感受一下这样的递归会慢到什么程度。
2、面试官期待的适用解法:
其实改进的方法比并不复杂。上述的递归代码之所以慢是因为重复的计算太多,我们只要想避免重复计算就型了。比如我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候我们先查找一下,如果前面已经计算过就不用重复计算了。
更简单的方法是从下往上计算,首先计算f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3)……依次类推就可以算出第n项了。很容易理解,这种思路的时间复杂度为O(n)。实现代码如下:
/** * 写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。 */ package swordForOffer; /** * @author JInShuangQi * * 2015年7月29日 */ public class E09Fibonacci { public long fibonacci(int n){ long result =0; long preOne = 1; long preTwo = 0; if( n == 0){ return preTwo; } if(n == 1){ return preOne; } for(int i = 2;i<= n ;i++){ result = preOne+preTwo; preTwo = preOne; preOne = result; } return result; } public static void main(String[] args){ E09Fibonacci fabonacci = new E09Fibonacci(); System.out.println(fabonacci.fibonacci(10)); } }
3、时间复杂度O(logn)但不够使用的解法)
通常面试到这里就差不多了,尽管我们还有比这更快的O(logn)解法,由于这种算法需要用到一个很生僻的数学公式,因此很少有面试官会要求我们掌握。不过以防万一,我们还是介绍一下这种算法。
我们先来聊i额一个数学公示:
解法比较:
用不同的方法求斐波那契数列的时间效率大不相同。第一种基于递归的解法虽然直观但时间效率太低,实际软件开发中不会使用这种方法,也不可能得到面试官的青睐。第二种方法把递归的算法用循环来实现,极大的提高了时间效率。第三种方法把求斐波那契数列转换成求矩阵的乘方,是一种很有创意的算法。虽然我们可以哟个O(logn)求的矩阵的n次方,但由于隐含的时间常熟较大,很少会有软件采用这种算法,另外,实现这种算法的代码也交复杂,不太适用于面试。
版权声明:本文为博主原创文章,未经博主允许不得转载。