csdn_spider/blog/ds19991999/原创-- Python数据结构(四)——递归.md

344 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 原创
Python数据结构——递归
# Python数据结构——递归
# 递归
递归是一种解决问题的方法,将问题分解为更小的子问题,直到得到一个足够小的问题可以被很简单的解决。通常递归涉及函数调用自身。递归允许我们编写优雅的解决方案,解决可能很难编程的问题。
## 计算整数列表和
```
# version1
def list_sum(num_list):
num_sum = 0
for i in num_list:
num_sum += i
return num_sum
print list_sum([1,3,5,7,9])
```
```
25
```
```
# version2:不使用循环
def list_num2(num_list):
if len(num_list)==1:
return num_list[0]
else:
return num_list[0]+list_num2(num_list[1:])
print list_num2([1,3,5,7,9])
```
```
25
```
## 递归三定律
## 整数转任意进制字符
```
def to_str(n,base):
conver_string = "0123456789ABCDEF"
if n < base:
return conver_string[n]
else:
return str(to_str(n//base,base))+conver_string[n%base]
print to_str(1453,16)
```
```
5AD
```
## 栈帧:实现递归
```
from pythonds.basic.stack import Stack
rStack = Stack()
def to_str(n,base):
conver_string = "0123456789ABCDEF"
while n>0:
if n<base:
rStack.push(conver_string[n])
else:
rStack.push(conver_string[n%base])
n //= base
res = ""
while not rStack.isEmpty():
res += str(rStack.pop())
return res
print to_str(1453,16)
```
```
5AD
```
## 可视化递归
```
# turtle 是 Python 所有版本的标准库,插图的工具
import turtle
```
```
myTurtle = turtle.Turtle()
myWin = turtle.Screen()
def drawSpiral(myTurtle,linelen):
if linelen > 0:
# 直走
myTurtle.forward(linelen)
# 右拐
myTurtle.right(90)
# 递归
drawSpiral(myTurtle,linelen)
drawSpiral(myTurtle,100)
# 调用函数 myWin.exitonclick(),这是一个方便的缩小窗口的方法,
# 使乌龟进入等待模式,直到你单击窗口,然后程序清理并退出。
myWin.exitonclick()
```
```
# 绘制分型树
import turtle
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)
def main():
t = turtle.Turtle()
myWin = turtle.Screen()
t.left(90)
t.up()
t.backward(100)
t.down()
t.color("green")
tree(75,t)
myWin.exitonclick()
main()
```
## 谢尔宾斯基三角形
```
import turtle
def drawTriangle(points,color,myTurtle):
myTurtle.fillcolor(color)
myTurtle.up()
myTurtle.goto(points[0][0],points[0][1])
myTurtle.down()
myTurtle.begin_fill()
myTurtle.goto(points[1][0],points[1][1])
myTurtle.goto(points[2][0],points[2][1])
myTurtle.goto(points[0][0],points[0][1])
myTurtle.end_fill()
def getMid(p1,p2):
return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]) / 2)
def sierpinski(points,degree,myTurtle):
colormap = ['blue','red','green','white','yellow',
'violet','orange']
drawTriangle(points,colormap[degree],myTurtle)
if degree > 0:
sierpinski([points[0],
getMid(points[0], points[1]),
getMid(points[0], points[2])],
degree-1, myTurtle)
sierpinski([points[1],
getMid(points[0], points[1]),
getMid(points[1], points[2])],
degree-1, myTurtle)
sierpinski([points[2],
getMid(points[2], points[1]),
getMid(points[0], points[2])],
degree-1, myTurtle)
def main():
myTurtle = turtle.Turtle()
myWin = turtle.Screen()
myPoints = [[-100,-50],[0,100],[100,-50]]
sierpinski(myPoints,3,myTurtle)
myWin.exitonclick()
main()
```
## 汉诺塔游戏
有一个印度教寺庙,将谜题交给年轻的牧师。在开始的时候,牧师们被给予三根杆和一堆 64 个金碟,每个盘比它下面一个小一点。他们的任务是将所有 64 个盘子从三个杆中一个转移到另一个。有两个重要的约束,它们一次只能移动一个盘子,并且它们不能在较小的盘子顶部上放置更大的盘子。牧师日夜不停每秒钟移动一块盘子。当他们完成工作时,传说,寺庙会变成灰尘,世界将消失。
虽然传说是有趣的,你不必担心世界不久的将来会消失。移动 64 个盘子的塔所需的步骤数是 2^64 -1 = 18,446,744,073,709,551,615264-1 = 18,446,744,073,709,551,615。以每秒一次的速度即584,942,417,355584,942,417,355 年!。
这里是如何使用中间杆将塔从起始杆移动到目标杆的步骤:
```
# 一开始盘子都在起始杆上
# Python 提供了我们需要调用的隐含的栈。
def moveTower(height,fromPole, toPole, withPole):
if height >= 1:
moveTower(height-1,fromPole,withPole,toPole)
moveDisk(fromPole,toPole)
moveTower(height-1,withPole,toPole,fromPole)
def moveDisk(fp,tp):
print("moving disk from",fp,"to",tp)
moveTower(4,'fromPole', 'toPole', 'withPole')
```
```
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'toPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'toPole', 'to', 'fromPole')
('moving disk from', 'toPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'fromPole')
('moving disk from', 'toPole', 'to', 'fromPole')
('moving disk from', 'withPole', 'to', 'toPole')
('moving disk from', 'fromPole', 'to', 'withPole')
('moving disk from', 'fromPole', 'to', 'toPole')
('moving disk from', 'withPole', 'to', 'toPole')
```
## 动态规划
假设你是一个自动售货机制造商的程序员。你的公司希望通过给每个交易最少硬币来简化工作。假设客户放入 1 美元的钞票并购买 37 美分的商品。你可以用来找零的最小数量的硬币是多少?
假设你的公司决定在埃尔博尼亚部署自动贩卖机,除了通常的 1510 和 25 分硬币,他们还有一个 21 分硬币 。
```
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-i)
if numCoins < minCoins:
minCoins = numCoins
return minCoins
print recMC([1,5,10,25],63)
```
```
6
```
这种算法是非常低效的。事实上,它需要 67,716,925 个递归调用来找到 4 个硬币的最佳解决 63 美分问题的方案。
减少我们工作量的关键是记住一些过去的结果,这样我们可以避免重新计算我们已经知道的结果。一个简单的解决方案是将最小数量的硬币的结果存储在表中。
```
def recMC(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 + recMC(coinValueList,change-i,knownResults)
if numCoins < minCoins:
minCoins = numCoins
knownResults[change] = minCoins
return minCoins
print recMC([1,5,10,25],63,[0]*64)
```
```
6
```
这个修改的算法减少了我们需要为四个硬币递归调用的数量63美分问题只需 221 次调用!
使用动态规划算法:
```
def dpMakeChange(coinValueList,change,minCoins):
for cents in range(change+1):
coinCount = cents
for j in [c for c in coinValueList if c<= cents]:
if minCoins[cents-j]+1<coinCount:
coinCounts = minCoins[cents-j]+1
minCoins[cents] = coinCount
return minCoins[change]
```
```
# 跟踪使用的硬币
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
def main():
amnt = 63
clist = [1,5,10,21,25]
coinsUsed = [0]*(amnt+1)
coinCount = [0]*(amnt+1)
print("Making change for",amnt,"requires")
print(dpMakeChange(clist,amnt,coinCount,coinsUsed),"coins")
print("They are:")
printCoins(coinsUsed,amnt)
print("The used list is as follows:")
print(coinsUsed)
```
```
main()
```
```
('Making change for', 63, 'requires')
(3, 'coins')
They are:
21
21
21
The used list is as follows:
[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]
```