尾递归

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

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

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

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); 
    }
}

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

  

 
posted @ 2019-04-02 10:17  十面埋伏但莫慌  阅读(293)  评论(0编辑  收藏  举报