第二章 递归
1、递归的概念
递归是一种powerful、wonderful、natural的(强有力的、精彩的、自然的)解决方法,可以解决一个大类的问题。
由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。
2、基本思想
通过将问题分解为与初始问题相同类型的子问题的方法来解决最终问题。每个子问题都可以被进一步的分解,直到可以通过简单明了的方法解决这一系列的问题。最后,将所有这些问题的解决综合起来,我们就获得了解决原始问题的方法。
另一种说明方式:能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。
当然,上面这些话有些拗口,总之以四个字来概括就是“分而治之”。
3、举例说明
(1)一个比较经典的对于递归的描述是“老和尚讲故事”,他说:从前有座山,山上有座庙,庙里有个老和尚在讲故事,他说从前有座山,山上有座庙,庙里有个老和尚在讲故事,他说从前有座山,……。这样没完没了地反复讲故事,直到最后老和尚烦了停下来为止。
反复讲故事可以看成是反复调用自身(递归的精髓就在于反复调用自身),但如果不能停下来那就没有意义了,所以最终还要能停下来。递归的关键在于找出递归方程式和递归终止条件(参见算法的五个特征之一:有穷性)。
(2)计算阶乘:
n的阶乘(记作n!)定义如下:
n!= n·(n-1)·(n-2)·……·2·1(n大于0) (若n=0,则n! = 1)
例如,5的阶乘5!=5x4x3x2x1=120。
分析:当n>0时,用f(n-1)来定义f(n),用f(n-1-1)来定义f(n-1)……,这是对递归形式的描述。当n=0时,f(n)=1,这是递归结束的条件。
对于递归问题,边界条件与递归方程是递归函数的二个要素,递归函数只有具备了这两个要素,才能在有限次计算后得出结果。
(3)计算裴波那契数列
裴波那契数列的定义:f(n)=f(n-1)+f(n-2); f(0)=1; f(1)=2
对应的递归程序为:
var n:integer;
function f(n:integer):longint;
begin
case n of
0:f:=1; {递归结束条件}
1:f:=2;
else
f:=f(n-1)+f(n-2) {递归调用}
end
end;
begin
readln(n);
writeln(f(n));
end.
4、递归小结
优点:结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算 法、调试程序带来很大方便。
缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。
典型反面事例:Joel的刷油漆算法。 (参见《Joel on software》那本书)