尾递归

Posted leo-lv

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尾递归相关的知识,希望对你有一定的参考价值。

    这两天在学快速排序,在查阅相关资料的时候,发现快速排序的花样实在是多,其中就有一种优化快速排序的方法用到了尾递归,我便又去了解了尾递归,因为接下来的一篇博客中写快速排序决定用上尾递归的方法,所以先记录下关于尾递归的知识。尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去。

以下是我对尾递归的个人见解: 

    所谓递归就是在调用一个会调用自身的函数,而我们在调用一个函数时,栈内存的操作如下:

1.系统会将函数中用到的各种参数、返回值等数据压进栈内存中。

2.然后当调用函数执行完,确认即将返回后,栈内存中的临时数据就会被释放消除。

   函数调用时栈内存的操作如图1所示。

技术图片

                                                    图1

那么当一个函数如下:


public static int fac(int n)
{
    if(n ==0 )
    {
        return 1;
    }
    if(n==1)
    {
        return 1;
    }
    else {
        return n*fac(n-1);
    }
}

   当上面这个函数被执行时,当程序执行到return (n*func(n-1));时,由于光有一个func(n-1)的返回值还不足以让这个函数返回,它还得乘一个这个函数内的n之后才能返回,所以栈内存就必须留着这个函数内的数据,一直等到func(n-1)的结果出来,然后跟这个函数内的n相乘完了才能确认返回,并释放栈,若是这个递归很深的话,很容易造成栈内存爆炸。技术图片

技术图片

图2

   当函数的最后执行代码除了调用函数自身外,不再执行其他运算,编译器会有这样的优化:

   函数在递归调用之前已经把所有的计算任务已经完毕了,他只要把得到的结果全交给子函数就可以了,无需保存什么,子函数其实可以不需要再去创建一个栈帧,直接把就着当前栈帧,把原先的数据覆盖即可。(注意不是所有编译器都有这样的优化。)

   尾递归例子程序如下:

public static int facTail(int n,int m)
{
    if(n==0)
    {
        return 1;
    }
    if(n==1)
    {
        return m;
    }
    else 
    {
        
        return facTail(n-1,m*n); 
    }
}

先写到这里,大佬们若发现我的理解有不妥之处,欢迎指正。

  

 

以上是关于尾递归的主要内容,如果未能解决你的问题,请参考以下文章

尾递归

尾递归

JavaScript函数尾调用与尾递归

函数式编程-尾递归尾调用

C语言 尾递归

尾递归