您的位置:首页 > 其它

输出斐波那契数列的第n项

2017-04-24 10:18 351 查看
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。

n<=39

斐波拉切数列简单的说就是0,1,1,2,3,5,8……,简单的说就是后一项是前两项的和,很容易想到使用递归。既简单又方便,几行代码就搞定了。于是有了如下的代码:

public class Solution {
public int Fibonacci(int n) {
if(n>39)
return -1;
if(n == 0){
return 0;
}else if(n == 1 || n == 2){
return 1;
}else{
return  Fibonacci(n-2) + Fibonacci(n- 1);
}
}
}


但是输出的结果就有问题了:



这里我试了一下条件改为(n>=39),复杂度是降低了,但是n=39这个测试用例肯定通不过,那么就发现 这个题精华的部分就在这个条件n=39,一定是程序还有优化的地方。

考虑一个简单的过程,n = 5的时候,具体的递归调用如下:

(1)Fibonacci(4) + Fibonacci(3);
(2)Fibonacci(3) + Fibonacci(2);
(3)Fibonacci(2) + Fibonacci(1);
(4)Fibonacci(1) + Fibonacci(0);


这个计算过程就像下图一样:



这里我们发现Fibonacci(3),Fibonacci(2),Fibonacci(1)都被重复的调用,重复调用意味着重复计算,会消耗栈空间和时间。

如果还要继续使用递归,而不使用循环,就要重新优化了。这个时候考虑尾递归。所谓的尾递归就是:

在计算机科学里,尾调用是指一个函数里的最后一个动作是一个函数调用的情形即这个调用的返回值直接被当前函数返回的情形。这种情形下称该调用位置为尾位置。若这个函数在尾位置调用本身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归,是递归的一种特殊情形。尾调用不一定是递归调用,但是尾递归特别有用,也比较容易实现。

尾调用的重要性在于它可以不在调用栈上面添加一个新的堆栈帧——而是更新它,如同迭代一般。尾递归因而具有两个特征: 调用自身函数(Self-called); 计算仅占用常量栈空间(Stack Space)。 而形式上只要是最后一个return语句返回的是一个完整函数,它就是尾递归。

由于尾递归没有开辟更多的栈空间,同时,只会存储当前的函数,而释放之前调用的函数,所以可以节省更多的空间。具体到本题,先给出代码:

public class Solution {
public int Fibonacci(int n) {
if(n>39)
return -1;
if(n == 0){
return 0;
}else if(n == 1 || n == 2){
return 1;
}else{
return  Fib(n,0,1);
}
}

private int Fib(int n,int a,int b){
if(n == 0){
return a;
}else{
return  Fib(n - 1,b,a + b);
}
}
}


这里代码可能不是很好理解,函数private int Fib(int n,int a,int b),最终返回的时候,是n==0,为什么呢?

看下图,执行过程:

Fib(5,0,1) -> Fib(4,1,1) -> Fib(3,1,2) -> Fib(2,2,3) -> Fib(1,3,5) -> Fib(0,5,8),当n为0的时候,b的值就是最终的计算结果。

我们调用这个函数的时候,要给这个函数传参数,也就是a =0, b = 1。

在看看迭代的解法:

int Fib(int n)
{
int num1 = 1;
int num2 = num1;
int num3 = num1;
while (n > 2)
{
num3 = num1 + num2;
num1 = num2;
num2 = num3;
n--;
}
return num3;
}


感觉和尾递归有一些思想有些相似
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐