再计算机程序中, 描述迭代的一种方法是使用循环,比如for 和while。另一种完全不同的方法就是递归 。
递归是一种技术,这种技术再通过一个函数在执行过程中一次或者多次调用器本身 。举一个具体的例子,如何用递归写一个函数用来求阶乘
n! = n(n-1)(n-2)···2x1
def factorial(n):
if n ==1 :
return 1
return n* factorial(n-1)
In: factorial(5)
Out: 120
可以看出,在执行factorial的过程中,调用了factorial自身,这就是递归。
实际上一切循环过程都可以用递归解决,Smalltalk语言就不支持循环结构,必须使用递归。
而通常使用for和while的思路是明确的,在满足循环条件时,执行循环语句 。换句话说,在满足循环停止条件时,不再执行循环语句。 而递归是自己调用自己,用这样的思路解决问题,思路好像不是很明确 。但只要确定基线条件和迭代条件就没问题了 。
递归条件,就是当该条件满足时,函数自己调用自己、
基线条件,就是当该条件满足时,函数不再调用自己(相当于while循环的停止条件)
比如,下面编写一个倒计时的函数
栈是由一系列对象组成的一个集合,这些对象满足后近先出(LIFO)的原则。 和自动贩卖机的那一列快乐肥宅水一样,也就是删除和插入都是最后一个。
执行递归的过程可以是调用栈的过程, 如下图, 调用的函数的执行内存都存放在栈对象中,执行完后被释放。
使用栈虽然很方便,但是也要付出代价:存储详尽的信息可能占用大量的内存。每个函数调
用都要占用一定的内存,如果栈很高,就意味着计算机存储了大量函数调用的信息。
分而治之是一种著名的递归方法 ,优雅而聪明,提供了一种解决问题的思路,是另一个可供你使用的工具。面对新问题时,你不再束手无策,而是自问:“使用分而治之能解决吗?” 分而治之的核心思路:
(1) 找出简单的基线条件;
(2) 确定如何缩小问题的规模,直到其符合基线条件。
举个例子更容易说清楚 , 下面定义一个的列表求和函数 。
首先确定基线条件,什么时候最简单呢,肯定是
list = [ ] ,sum(list) = 0 。
不断的分解列表,直到符合基线条件 。
def sum_list(list):
if list == []:
return 0
else :
return list[0] + sum_list(list[1:])
In:sum_list([2,3,1,4])
Out: 10
最经典的分而治之的例子就是递归法解决快速排序 。
什么样的情况对于排序算法最简单呢 ?
上述两种作为基线条件, 下一步将其他的情况不断缩小问题规模直到达到基线条件。
如果是两个以上的列表,就用第一个元素的值为基准线将列表拆开, [2,0,3] 拆为 [2], [0], [3], 每个部分都排序后再拼接。更大长度的列表,以此类推 。
def quicksort(List): # DIVIDE AND CONQUER
if len(List)<= 1:
return List
elif len(List)==2 :
if List[0]>List[1]:
return List[::-1] # 调换次序
else: return List
else:
left_list = [i for i in List if i<List[0]]
right_list = [i for i in List if i >List[0]]
return quicksort(left_list)+ List[:1] + quicksort(right_list)
In: quicksort([3,2,1,5,6,4])
Out: [1, 2, 3, 4, 5, 6]