算法之尾递归

什么是尾递归?就是函数返回之前的最后一个操作若是递归调用,则该函数进行了尾递归。先看一个例子:

例子1:阶乘

//阶乘递归实现
int FactorialRecursion(int n)
{
	if(n==0) retrun 1;
	else retrun n*FactorialRecursion(n-1);
}
//尾递归实现
int FactorialTailRecursion(int n, int acc)
{
    if (n == 0) return acc;
    return FactorialTailRecursion(n - 1, acc * n);
}
//循环实现
int FactorialLoopOptimized(int n, int acc)
{
    while (true)
    {
        if (n == 0) return acc;
 
        acc *= n;
        n--;
    }
}

阶乘的递归实现,函数返回之前的最后一个操作为乘法,不是递归调用,所以不是尾递归。

尾递归与循环:由于递归在方法的末尾,因此方法中的局部变量已经毫无用处,编译器完全可以将其“复用”,并把尾递归优化为“循环”方式。所以尾递归的方式,类似循环。也可以说,尾递归可以转换为循环来实现。据网上查阅,java,C#和python都不支持编译环境自动优化尾递归,但是对于C语言来说,编译器白提供的服务,用了也不差,毕竟递归代码会好理解一点,但换句话说,如果写到尾递归这份上了,变成非递归已经很好实现了,完全可以用循环来搞定,所以呢,这个时候,就看个人喜好了。

例子2:斐波那契问题

//递归实现
int fabonacci(int n)//复杂度为指数级
{
	if(n==0) 	return 0;
	else if(n==1) 	return 1;
	else 
	{
		return fabonacci(n-1)+fabonacci(n-2);
	}
}
//循环实现
int fabonacci2(int n)//复杂度:O(n)
{
	int fab;
	int a=1,b=1;
	if(n==0) 	return 0;
	else if(n==1) 	return 1;
	else
	{	
		for(int i=2;i<=n;i++)
		{
			fab=a+b;
			a=b;
			b=fab;
		}
	}
	return fab;
}
//斐波那契尾递归实现
int fabtrail(int n,int a,int b)
{
	if(n==0) return a;
	else if(n==1) return b;
	else return fabtrail(n-1,b,a+b);
}

编译器是怎样优化尾递归的?

我们知道递归调用是通过栈来实现的,每调用一次函数,系统都将函数当前的变量、返回地址等信息保存为一个栈帧压入到栈中,那么一旦要处理的运算很大或者数据很多,有可能会导致很多函数调用或者很大的栈帧,这样不断的压栈,很容易导致栈的溢出。

我们回过头看一下尾递归的特性,函数在递归调用之前已经把所有的计算任务已经完毕了,他只要把得到的结果全交给子函数就可以了,无需保存什么,子函数其实可以不需要再去创建一个栈帧,直接把就着当前栈帧,把原先的数据覆盖即可。相对的,如果是普通的递归,函数在递归调用之前并没有完成全部计算,还需要调用递归函数完成后才能完成运算任务,比如return n * FactorialRecursion(n - 1);这句话,这个FactorialRecursion(n)在算完FactorialRecursion(n-1)之后才能得到n * FactorialRecursion n - 1)的运算结果然后才能返回。

综上所述,编译器对尾递归的优化实际上就是当他发现你在做尾递归的时候,就不会去不断创建新的栈帧,而是就着当前的栈帧不断的去覆盖,一来防止栈溢出,二来节省了调用函数时创建栈帧的开销,用《算法精解》里面的原话就是:“When a compiler detects a call that is tail recursive, it overwrites the current activation record instead of pushing a new one onto the stack.”

问题:

优化工作交给编译器还是交给自己?

这个怎么说呢,据网上查阅,java,C#和python都不支持编译环境自动优化尾递归,这种情况下,当然是别用递归效率最高,可以看下这里http://www.cnblogs.com/Alexander-Lee/archive/2010/09/16/1827587.html(利用抛出异常,自己捕获,优化尾递归)

注:http://www.cnblogs.com/JeffreyZhao/archive/2009/04/01/tail-recursion-explanation.html(老赵-尾递归优化)

这里提出了Continuation技术,不懂

你可能感兴趣的:(算法之尾递归)