递归的思想是把问题分解成为规模更小且与原问题有着相同解法的问题,那么是不是这样的问题都能用递归来解决呢?答案是否定的。并不是所有问题都能用递归来解决。那么什么样的问题可以用递归来解决呢?一般来讲,能用递归来解决的问题必须满足两个条件:
1.可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式;如果一个问题不满足以上两个条件,那么它就不能用递归来解决。
递归分为两个阶段:
1)递推:把复杂的问题的求解推到比原问题简单一些的问题的求解;
2)回归:当获得最简单的情况后,逐步返回,依次得到复杂的解。
在编写递归调用的方法的时候,最好把对简单情境的判断写在最前面,以保证函数调用在检查到简单情境的时候能够及时地中止递归,否则,你的函数可能会永不停息的在那里递归调用了。
由于递归引起一系列的函数调用,并且有可能会有一系列的重复计算(如递归计算斐波那契数列),递归算法的执行效率相对较低,浪费空间,并且递归太深容易造成堆栈的溢出。所以有时可以将某些递归转换成为非递归的形式。递归转换为非递归的情况有两种:
1.尾递归可以转变为迭代循环处理;
2.可以自己建立一个堆栈保存这些局部变量,替换系统栈。
尾递归。如果在递归函数中,递归调用返回的结果总被直接返回,则称为尾递归。某些一般递归可以改写为尾递归(如文后例子中斐波那契数列的尾递归形式),尾递归是对一般递归的优化,尾递归和一般递归的差别体现在参数上,本质是将上一次计算的结果记录起来,通过参数传递给下次调用。一般递归如果调用层次过多,栈就容易满造成溢出,尾递归可以消除函数调用栈过长的问题,但是必须借助编译器,每一个函数在调用下一个函数之前,都能做到先把当前自己占用的栈给先释放了,尾递归的调用链上可以做到只有一个函数在使用栈,此时栈可以一直使用。利用尾递归最主要的目的是优化,比一般递归节省了空间和时间。尾递归还容易优化成为普通循环。
迭代 是用计算机解决问题的一种基本方法,可以算是循环的一种。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。所以迭代算法有三方面:(1)确定迭代变量;(2)建立迭代关系式。所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成;(3)对迭代过程进行控制。在什么时候结束迭代过程,这是编写迭代程序必须考虑的问题,不能让迭代过程无休止地重复执行下去。
尾递归可以用循环代替,转变后能克服递归的一些不足。例如:计算阶乘的递归计算过程属于线性递归,步骤数目的增长正比于输入n。也就是说,这个过程所需步骤的增长为O(n) ,空间需求的增长也为O(n) 。对于迭代的阶乘,步数还是O(n)而空间是O(1) ,也就是常数。
不需要消解的递归
那种盲目的消解递归,不惜一切代价躲避递归,认为“递归的速度慢,为了提高速度,必须用栈或者其他的方法来消解的说法是很片面的。如果一个递归过程用非递归的方法实现后,速度提高了,那只是因为递归做了一些无用功。假使一个递归过程必须要用栈才能消解,那么完全模拟后的结果根本就不会对速度有任何提升,只会减慢;如果你改完后速度提升了,那只证明你的递归函数写的有问题,如多了许多重复操作——打开关闭文件、连接断开数据库,而这些完全可以放到递归外面。如果把递归都翻译成迭代或借助栈的形式,就往往会模糊了程序的本质,以致使它非常难以理解。它们本质上是递归的,没有简单的迭代形式。这样的递归算法不宜转化为非递归算法。
例子:
public class TailRecursion {
//阶乘的一般递归
static int factorial(int n)
{
if (n <= 1) return 1;
return (n * factorial(n-1));
}
//阶乘的尾递归形式
static int factorial_tail(int n, int res)
{
if (n <= 1) return res;
return factorial_tail(n - 1, n * res);
}
//阶乘的迭代形式
static int factorial_loop(int n)
{
int r = 1;
for(int i=1;i<=n;i++){
r=r*i;
}
return r;
}
//斐波那契数列的一般递归
static int fibonacci(int n)
{
if(n<=3)
return 1;
else{
return fibonacci(n-1)+fibonacci(n-2);
}
}
//斐波那契数列的尾递归形式
static int fibonacci_tail(int n,int acc1,int acc2)
{
if (n < 2)
{
return acc1;
}
else
{
return fibonacci_tail(n-1,acc2,acc1+acc2);
}
}
//斐波那契数列的循环方式
static int fibonacci_loop(int n)
{
int a=0;
int b=1;
if(n==1)
return 1;
if(n==2)
return 1;
for(int i=3;i<=n;i++){
int temp=a+b;
a=b;
b=temp;
}
return b;
}
public static void main(String[] args) {
int j=factorial_loop(5);
System.out.println(j);
int r=fibonacci_tail(8,0,1);
System.out.println(r);
}
}
参考资料:http://www.nowamagic.net/librarys/veda/detail/2322