本系列为《Python数据结构与算法分析》第二版学习笔记,作者:布拉德利.米勒;戴维.拉努姆。
递归是解决问题的一种方法,它将问题不断地分成更小的问题,直到子问题可以用普通的方法解决。通常情况下,递归会使用一个不停调用自己的函数。
一、递归三原则
(1)递归算法必须有基本情况;
(2)递归算法必须改变其状态并向基本情况靠近;
(3)递归算法必须递归的调用自己。
基本情况是指使算法停止递归的条件,这通常是小到足以直接解决的问题。为了遵守第二条原则,必须设法改变算法的状态,从而使其向基本情况靠近。改变状态是指修改算法所用的某些数据,这通常意味着代表问题的数据以某种方式变得更小。最后一条原则是递归算法必须对自身进行调用,这正是递归的定以。递归的逻辑并不是循环,而是将问题分解成更小、更容易解决的子问题。
二、用递归计算一列数之和
假设需要计算数字列表[1, 3, 5, 7, 9]的和,一般用循环求和可以解决。
def listsum(numList):
theSum = 0
for i in numList:
theSum += i
return theSum
如何利用递归解决这个问题,数字列表numList的总和等于列表中的第一个元素(numList[0])加上其余元素(numList[1:])之和,可以用函数的形式来表述这个定以:
listSum(numList) = first(numList) + listSum(rest(numList))
first(numList)返回列表中的第一个元素,rest(numList)则返回其余元素,代码实现如下:
def listsum(numList):
if len(numList) == 1:
return numList[0]
else:
return numList[0] + listsum(numList[1:])
这里,第2行检查列表是否只包含一个元素,这个检查很重要,同时也是该函数的退出语句,满足原则1。对于长度为1的列表,其元素之和就是列表中的数。其次,listsum函数在第5行调用了自己,这就是将listsum称为递归函数的原因。
三、将整数转换为任意进制的字符串
前面我们用while循环解决过这个问题,这里我们用递归来处理。
以十进制整数769为例,假设有一个字符序列对应前10个数,比如convString = “0123456789”。若将一个小于10的数字转换成其对应的字符串,只需在字符序列中查找对应的数字即可。上述算法情况说明,整个算法包含三个组成部分:
(1)将原来的整数分成一系列仅有单数位的数;
(2)通过查表将单数位的数转换成字符串;
(3)连接得到的字符串,从而形成结果。
rStack = Stack()
def toStr(n, base):
convertString = "0123456789ABCDEF"
if n < base:
rStack.push(convertString[n])
else:
rStack.push(convertString[n % base])
toStr(n // base, base)
四、用递归画谢尔平斯基三角形
手动绘制谢尔平斯基三角形过程如下:从一个大三角形开始,通过连接每条边的中点将它分割成四个新的三角形;然后忽略中间的三角形,利用同样的方法分隔其余三个三角形。
代码如下:
class SierPinski():
def __init__(self):
pass
def drawTriangle(self, points, color, myTurtle):
myTurtle.fillcolor(color)
myTurtle.up()
myTurtle.goto(points[0])
myTurtle.down()
myTurtle.begin_fill()
myTurtle.goto(points[1])
myTurtle.goto(points[2])
myTurtle.goto(points[0])
myTurtle.end_fill()
def getMid(self, p1, p2):
return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)
def sierpinski(self, points, degree, myTurtle):
colormap = ['blue', 'red', 'green', 'white', 'yellow', 'violet', 'orange']
self.drawTriangle(points, colormap[degree], myTurtle)
if degree > 0:
self.sierpinski([points[0],
self.getMid(points[0], points[1]),
self.getMid(points[0], points[2])],
degree-1, myTurtle)
self.sierpinski([points[1],
self.getMid(points[0], points[1]),
self.getMid(points[1], points[2])],
degree - 1, myTurtle)
self.sierpinski([points[2],
self.getMid(points[2], points[1]),
self.getMid(points[0], points[2])],
degree - 1, myTurtle)
if __name__ == '__main__':
SierPinski = SierPinski()
myTurtle = Turtle()
myWin = myTurtle.getscreen()
myPoints = [(-500, -250), (0, 400), (500, -250)]
SierPinski.sierpinski(myPoints, 4, myTurtle)
myWin.exitonclick()