尾递归
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);
}
}
先写到这里,大佬们若发现我的理解有不妥之处,欢迎指正。
以上是关于尾递归的主要内容,如果未能解决你的问题,请参考以下文章