个人觉得递归的运用最能显示程序员的编程能力和问题抽象归纳能力,将一个复杂的问题简单化。递归是解决复杂问题的利器。刚学编程的时候对递归总是一知半解,随着慢慢的学习发现要想灵活运用递归,对问题的本质的把握至关重要,只有掌握问题的本质才能正确运用递归。运用递归也是锻炼程序员数学归纳的能力。有些问题如果按照正常的思维去思考,会陷入一个死胡同。而换一个角度,却发现问题如此简单。读《编程之美》的最大感悟是:把握问题的本质,将复杂问题分解成简单的问题去考虑,换个角度去考虑。印象特别深刻的是 “蚂蚁爬杆”的问题 发现换个角度原来问题真的很简单。所以如果遇到无法解决的问题的时候不妨换个角度考虑。
“GNU” "GNU is Not Unix" 名字就是一个递归。
递归简单点说就是"自己定义自己"
我想我们最初学习递归的时候典型的例子就是 "汉诺塔"问题吧。
有三根细柱子(A,B,C) A柱上套着6个圆盘。这些盘大小各异,按从大到小的顺序自下而上摆放。
现在要把套着A柱上的6个圆全部移动到B柱子上。并且在移动圆盘的时候遵守以下规则:
一次只能移动柱子最上端的一个圆盘
小圆盘不能放大圆盘
将一个圆盘从一个柱子移到另一根柱子,算移动1次.那么,将6个圆盘全部从A移动到B需要移动多少次。
问题分析:如果我们一开始就考虑6个圆盘的话头脑肯定会混乱,所以我们先缩小问题的规模,从3个圆盘开始考虑。即暂不考虑6个圆盘的"6层汉诺塔",而是先找出“3层汉诺塔”的解法。
3层汉诺塔的问题的解法。
仔细思考 3层汉诺塔 的解法就能找到解决“6层汉诺塔”的问题的方法啦。如果不明白的话,请在思考一下“4层汉诺塔”和“5层汉诺塔”。
在来回移动圆盘的过程中,你一定会觉得“在重复做相似的事情”。之所以会产生这种感觉是因为我们有 “发现规律的能力”。这种感觉很重要,是解决递归问题的关键。
在上图中。比较 1,2,3和5,6,7
1,2,3中,移动3次将2个圆盘从A柱移动到C柱子。
5,6,7中,移动3次将2个圆盘从C柱子移动到B柱子
我们把3个圆盘 看层2个圆盘去看待,规律就更加明显。
这样看是不是很明显,我们把 所有的问题都可以转换为 "2层汉诺塔"的解决思路。 n层汉诺塔 看成 最下面 1 个圆盘与 上面 (n-1) 个圆盘。
"6层汉诺塔"问题 可以通过一下步骤求出:
(1)首先,将5个圆盘从A柱移动到C柱子(解出5层汉诺塔问题)
(2)然后, 将6个之中最大的圆盘从A柱移动到B柱子
(3)最后, 将5个圆盘从C柱移到B柱子(解出5层汉诺塔)
用同样的思路可以解决"5层汉诺塔"问题。
(1)首先,将4个圆盘从A柱移动到C柱子(解出4层汉诺塔问题)
(2)然后, 将5个之中最大的圆盘从A柱移动到B柱子
(3)最后, 将4个圆盘从C柱移到B柱子(解出4层汉诺塔)
“4层汉诺塔”,“3层汉诺塔”,............也是同样的解法。“1层汉诺塔”只要移动一次圆盘就完成啦。
通过这种思考方式,我们可以总结出 “n层汉诺塔”的解法。
以下,我们不使用A,B,C这三根柱子的具体名称,而将其设为x,y,z。因为x,y,z在不同的情况会不固定对应A,B,C中的某一个。x为起点柱子,y为目标柱,C为中转柱。
" 解出n层汉诺塔"的步骤,即“利用Z柱将n个圆盘从x柱转移至y柱”具体如下:
将n个圆盘从x柱,经由z柱中转,移到y柱:
当n = 0时:
不做什么动作
当n > 0时:
首先,将n-1个圆盘从x柱,经由y柱中转,移到z柱(解出n-1层汉诺塔)
然后,将1个圆盘从x柱移到y柱。
最后,将n-1个圆盘从z柱,经由x柱中转,移到y柱(解出n-1层汉诺塔)
程序代码:
def hanoi(n, x, y, z): if n == 0: return hanoi(n - 1, x, z, y) #从x柱,经由y柱中转,移到z柱 print '%s -> %s' % (x, y) hanoi(n - 1, z, y, x) #从z柱,经由x柱中转,移到y柱
我们要解决 "n层汉诺塔" 最少移动的次数
H(n)
移动0个圆盘的次数为0
H(0) = 0
移动1个圆盘的次数为1
H(1) = 1
根据n层汉诺塔所要的步骤,可以将移动次数H(n)写成:
0 (n = 0)
H(n)
H(n - 1) + 1 + H(n - 1) (n >0)
按照下述方式,思考n=1,2,3......时的式子可能更容易理解
用程序表示:
def hanoiNum(n): if n == 0: return 0 return hanoiNum(n - 1) + 1 + hanoiNum(n -1)
分别求出n = 0, 1,2,3,4...
H(n) = 0,2,3,7,15
我们可以归纳一下表达式:
H(n) = 2n - 1。
递归让我们只关注问题的本质而不需要关心细节问题。