递归和尾递归的比较,斐波那契

相信若是一我的让咱们求一个斐波那契数列,若是你学过c语言,你必定会说用递归法啊,很容易就实现了,可是若是人家让你求斐波那契的第50个数,并且你对递归了解的话,估计帮你不会说递归了,若是了解够深的话,其实你会说递归也能够求出来。
一、递归
       首先咱们来讲说什么是递归,简单的来讲,就是一个函数须要调用本身来完成某种功能,这种调用就叫作递归。
       但咱们须要清楚一点,递归在使用的时候,并非一直调用本身,咱们须要给他一个停下来的时机。就像打仗同样,要知道进攻的路线,但若是遇到突发情况也要能及时撤退。因此咱们的递归也同样,你须要给他一条前进路径也要给他一条返回路径。因此 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
      但咱们都清楚,递归有它致命的缺点,在递归调用的过程中系统为每一层的返回点、局部量等开辟了栈来存储,所以递归次数过多容易形成栈溢出,正是由于如此它和循环比较效率是很低的,这也就是我前面所说的若是让你计算斐波那契的第五十个数你直接用递归法去求实际上是不太好的。
 
下面咱们来看看程序:
int Fib(int n)
{
    if( n < 2)
         return n;
     return (Fib(n-1)+Fib(n-2));
}
这样写出来的代码很简洁,来分析一下它的执行过程,咱们给n=5:
可能这样你还看不出问题,其实上面的图至关是一个树状结构:
     
   红色的部分在以后又会被求到,若是咱们给的数值不是5是一个更大的数,则被重复计算和调用的数和次数会变得更多。可见,在这样一个过程当中,咱们把某些值一直在重复计算,再加上重复的开辟栈空间,使得它的效率变得很是低,大家能够试着求一下第40 50个斐波那契额。
 
 
二、尾递归
   尾部递归是一种编程技巧。若是在递归函数中,递归调用返回的结果总被直接返回,则称为尾部递归。尾部递归的函数有助将算法转化成函数编程语言,并且从编译器角度来讲,亦容易优化成为普通循环。这是由于从电脑的基本面来讲,全部的循环都是利用重复移跳到代码的开头来实现的。若是有尾部归递,就只须要叠套一个堆栈,由于电脑只须要将函数的参数改变再从新调用一次。
复制代码
 1 int Fib(int n, int ret1, int ret2)
 2  
 3 {
 4     if (n ==0 )
 5     {
 6         return ret1;
 7     }
 8     else
 9     {
10  return Fib(n - 1, ret2, ret1 +ret2);
11     }
12 }
复制代码

 

 
它的执行步骤以下,每次的ret1就是要求当前的返回值,当执行到n减到0的时候,此时的ret1就是咱们要求的第n个数:
 
 
这里咱们在传参的时候须要传ret=0,ret2=1; Fib(n - 1, ret2, ret1 +ret2)的使用,本来朴素的递归产生的栈的层次像二叉树同样,以指数级增加,可是如今栈的层次却像是数组,变成线性增加了,实在是奇妙,总结起来也很简单,本来栈是先扩展开,而后边收拢边计算结果,如今却变成在调用自身的同时经过参数来计算。
ret1就是第n个数,而ret2就是第n与第n+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;
}
能够看出迭代法实现的方法其实和咱们的尾递归法一个道理,可是迭代法比较通俗易懂,并且和尾递归比较起来,由于不用开辟栈空间,因此这三种方法比较起来迭代法是效率最高的。咱们在解决实际问题的时候,根据实际要求选择合适的方法。
相关文章
相关标签/搜索