递归
递归三原则:
1)递归算法必须有基本情况
2)递归算法必须改变其状态并向基本情况靠近
3)递归算法必须递归地调用自己
数组求和函数的递归实现:
def listsum(numlist):
if len(numlist) == 1:
return numlist [0]
else:
return numlist[0] + listsum(numlist[1:])
将整数转换成2~16为进制基数的字符串。
def toStr(n, base):
convertString = "0123456789"
if n < base:
return convertString[n]
else:
return toStr(n//base, base) + convertString[n%base]
使用栈帧实现递归
栈中的返回值取代了累加变量
栈帧限定了函数所用变量的作用域。尽管反复调用相同的函数,但是每次调用都会为函数的局部变量创建新的作用域。
from pythonds.basic import Stack
rStack = Stack()
def toStrnew(n, base):
convertString = "0123456789ABCDEF"
if n < base:
rStack.push(convertString[n])
else:
rStack.push(convertString[n % base])
toStrnew(n // base, base)
递归可视化:
用turtle模块递归地绘制螺旋线
from turtle import *
myTurtle = Turtle()
myWin = myTurtle.getscreen()
def drawSpiral(myTurtle, lineLen):
if lineLen > 0:
myTurtle.forward(lineLen)
myTurtle.right(90)
drawSpiral(myTurtle, lineLen-5)
drawSpiral(myTurtle, 200)
myWin.exitonclick()
绘制分形树
def tree(branchLen, t):
if branchLen > 5:
t.forward(branchLen)
t.right(20)
tree(branchLen-15, t)
t.left(40)
tree(branchLen-10, t)
t.right(20)
t.backward(branchLen)
from turtle import *
t = Turtle()
myWin = t.getscreen()
t.left(90)
t.up()
t.backward(300)
t.down()
t.color('green')
tree(110, t)
myWin.exitonclick()
找零问题
找零问题的暴力解决方案
首先确定基本情况: 如果要找的零钱金额与硬币面值相同,那么只需要找1枚硬币即可。
如果要找的零钱金额和硬币的面值不同,则有多种选择:
1)1枚1分硬币加上找零金额减去1分之后所需的硬币
2)1枚5分硬币加上找零金额减去5分之后所需的金币
3)1枚10分硬币加上找零金额减去10分之后所需的金币
4)1枚25分硬币加上找零金额减去25分之后所需的金币
def recMC(coinValuelist, change):
minCoins = change
if change in coinValuelist:
return 1
else:
for i in [c for c in coinValuelist if c <= change]:
numCoins = 1 + recMC(coinValuelist, change - 1)
if numCoins < minCoins:
minCoins = numCoins
return minCoins
这个方法执行了过多重复的步骤
添加查询表之后的找零算法
def recDC(coinValuelist, change, knownResults):
minCoins = change
if change in coinValuelist:
knownResults[change] = 1
return 1
elif knownResults[change] > 0:
return knownResults[change]
else:
for i in [c for c in coinValuelist if c <= change]:
numCoins = 1 + recDC(coinValuelist, change-1, knownResults)
if numCoins < minCoins:
minCoins = numCoins
knownResults[change] = minCoins
return minCoins
动态规划算法
尽管我们一开始使用递归方法来解决找零问题,但是dpMakeChange并不是递归函数
能够用递归方法解决问题,并不代表递归方法是最好或最高效的方法。
def dpMakeChange(coinValuelist, change, minCoins):
for cents in range(0, change+1):
coinCount = cents
for j in [c for c in coinValuelist if c <= cents]:
if minCoins[cents-j] + 1 < coinCount:
coinCount = minCoins[cents - j]+1
minCoins[cents] = coinCount
return minCoins[change]
尽管找零算法在寻找最少硬币数时表现出色,但是由于没有记录所用的硬币,因此它并不能帮助我们进行世纪的找零工作。通过记录minCoins表中每一项所加的硬币,可以轻松扩展dpMakeChange,从而记录所用的硬币。
如果知道上一次加的硬币,便可以减去其面值,从而找到表中前一项,并通过它知晓之前所加的硬币。
def dpMakeChange(coinValuelist, change, minCoins, coinsUsed):
for cents in range(change+1):
coinCount = cents
newCoin = 1
for j in [c for c in coinValuelist if c<= cents]:
if minCoins[cents-j] + 1 <coinCount:
coinCount = minCoins[cents - j]+1
newCoin = j
minCoins[cents] = coinCount
coinsUsed[cents] = newCoin
return minCoins[change]
def printCoins(coinsUsed, change):
coin = change
while coin >0:
thisCoin = coinsUsed[coin]
print(thisCoin)
coin = coin - thisCoin
c1 = [1,5,10,21,25]
coinsUsed = [0]*64
coinCount = [0]*64
print(dpMakeChange(c1, 63, coinCount, coinsUsed))
print(printCoins(coinsUsed, 30))
print(coinsUsed)
3
5
25
[1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 10, 1, 1, 1, 1, 5, 1, 1, 1, 1, 10, 21, 1,
1, 1, 25, 1, 1, 1, 1, 5, 10, 1, 1, 1, 10, 1, 1, 1, 1, 5, 10, 21, 1, 1,
10, 21, 1, 1, 1, 25, 1, 10, 1, 1, 5, 10, 1, 1, 1, 10, 1, 10, 21]