1. 为什么需要递归
我觉得有一种说法比较容易理解:利用递归可以实现无限循环体。
即当面对循环次数不定的问题时,递归可以简洁的给出问题的解答。递归的作用当然不止于此,但是光这一点就已经使得它足够重要了。
2. 递归思想的难点
个人觉得编写递归算法的难点就在于这种思维方式与人类的一般思维方式相逆,我们习惯于从问题解决过程的 initial state 开始思考问题,而使用递归就要趋向于从 last state (simplest state)开始思考:假设前面 n-1 步都完成了,第 n 步应该怎么做。以最简单的阶乘问题为例:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
这个问题的思考过程就是前 n-1 个整数的阶乘已经完成了,那么最后一步就是用 n 乘上这个结果。函数不断调用自身,使得其状态不断接近基本情况(出口),最后到达出口(n == 0)结果回调得到问题的答案。
再比如汉诺塔问题:
def hanoi(n, fromPole, toPole, withPole):
if n >= 1:
hanoi(n-1, fromPole, withPole, toPole)
moveDisk(fromPole, toPole)
hanoi(n-1, withPole, toPole, fromPole)
def moveDisk(fromPole, toPole):
print('from:', fromPole, 'to:', toPole)
if __name__ == '__main__':
hanoi(3, 'A', 'C', 'B')
这里 A、B、C 分别代表起始杆,中间杆和目标杆。假设移动前 n-1 个环的方法已经实现,思考最后一步应该做什么:先把 n-1 个环移到 B 上,只有最后一个最大的环还在 A 上,移动这个最大的环到 C 上,最后把 B 上的 n-1 个环移到 C 上。函数不断调用自身,直到没有环的情况。
归并排序:
def mergeSort(alist):
if len(alist) <= 1:
return alist
else:
mid = len(alist) // 2
left = alist[:mid]
right = alist[mid:]
mergeSort(left)
mergeSort(right)
i, j, k = 0, 0, 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
alist[k] = left[i]
i += 1
else:
alist[k] = right[j]
j += 1
k += 1
while i < len(left):
alist[k] = left[i]
i += 1
k += 1
while j < len(right):
alist[k] = right[j]
j += 1
k += 1
return alist
if __name__ == '__main__':
print(mergeSort([5,6,4,3,0,9,7]))
在左半部分列表和右半部分列表上调用 mergeSort 函数,则认为这两部分它们已经被排序,那么当前要做的就是把这两部分合并,即按顺序挑选两部分中最小的项赋给结果列表。
3. 递归三定律
- 递归算法必须具有基本情况(递归出口)
- 递归算法必须逐步改变其状态并向基本情况靠近
- 递归算法必须以递归方式调用自身
4. 缺点
递归的缺点很明显,就是对于空间资源的过分消耗。因此在实际开发过程中,简单说来就是:能不用递归就不要用