5.Python数据结构及算法----递归

什么是递归

递归是一种解决问题的方法,将问题分解为更小的子问题,直到得到一个足够小的问题可以被很简单的解决。通常递归涉及函数调用自身。递归允许我们编写优雅的解决方案,解决可能很难编程的问题。

计算整数列表和

我们将以一个简单的问题开始,你已经知道如何不使用递归解决。 假设你想计算整数列表的总和。

def sumList(inputlist):
    if len(inputlist) == 1:
        return inputlist[0]
    else:
        return inputlist[0] + sumList(inputlist[1:])

print(sumList([1, 3, 5, 7]))

递归的三定律

像阿西莫夫机器人,所有递归算法必须服从三个重要的定律:

  1. 递归算法必须具有基本情况。
  2. 递归算法必须改变其状态并向基本情况靠近。
  3. 递归算法必须以递归方式调用自身。

整数转换为任意进制字符串

假设你想将一个整数转换为一个二进制和十六进制字符串。例如,将整数 10 转换为十进制字符串表示为 10,或将其字符串表示为二进制 1010。虽然有很多算法来解决这个问题,包括在栈部分讨论的算法,但递归的解决方法非常优雅。

def toStr(n, base):
    converString = '0123456789ABCDEF'
    if n < base:
        return converString[n]
    else:
        return toStr(n // base, base) + converString[n % base]

print(toStr(33, 8))

栈帧:实现递归

假设不是将递归调用的结果与来自 convertString 的字符串拼接到 toStr,我们修改了算法,以便在进行递归调用之前将字符串入栈。

# 实现栈功能
class StackTest:
    def __init__(self):
        self.items = [] # 构造函数创建栈

    def isEmpty(self):
        return self.items == [] # 返回栈是否为空

    def push(self, item):
        return self.items.append(item) # 栈追加元素

    def pop(self):
        return self.items.pop() # 弹出最后一个元素

    def peek(self):
        return self.items[-1] # 返回最后一个元素

    def size(self):
        return len(self.items) # 返回栈的长度

def toStr(n, base):
    convertString = '0123456789ABCDEF'
    while n > 0:
        if n < base:
            rStack.push(convertString[n])
        else:
            rStack.push(convertString[n % base])

        n = n // base

    res = ''
    while not rStack.isEmpty():
        res = res + str(rStack.pop())
    return res

if __name__ == '__main__':
    rStack = StackTest()
    print(toStr(10, 2))

可视化递归

我们将使用 turtle 模块递归绘制螺旋。

这个简单函数的基本情况是当我们想要绘制的线的长度(由 len 参数给出)减小到零或更小时。如果线的长度大于零,我们让点以 len 单位前进,然后向右转 90 度。当我们再次调用 drawSpir​​al 并缩短长度时递归。在ActiveCode 1 结束时,你会注意到我们调用函数 myWin.exitonclick(),这是一个方便的缩小窗口的方法,使点进入等待模式,直到你单击窗口,然后程序清理并退出

import turtle

def drawSpiral(myTurtle, length):
    if length > 0:
        myTurtle.forward(length)
        myTurtle.right(90)
        drawSpiral(myTurtle, length-5)

if __name__ == '__main__':
    myTurtle = turtle.Turtle()
    myWin = turtle.Screen()
    drawSpiral(myTurtle, 100)
    myWin.exitonclick()

我们如何使用绘图来生成分形树。让我们更仔细地看一下代码。你会看到在第 5 行和第 7 行,我们正在进行递归调用。在第 5 行,我们在点向右转 20 度之后立即进行递归调用;这是上面提到的右树。然后在第 7 行,点进行另一个递归调用,但这一次后左转 40 度。点必须向左转 40 度的原因是,它需要撤消原来的向右转 20 度,然后再向左转 20 度,以绘制左树。还要注意,每次我们对树进行递归调用时,我们从 branchLen 参数中减去一些量; 这是为了确保递归树越来越小。你还应该看到到第 2 行的初始 if 语句是检查 branchLen 的基本情况大小。

import turtle

def tree(branchlen, t):
    if branchlen > 5:
        t.forward(branchlen)
        t.right(20)
        tree(branchlen - 15, t)
        t.left(40)
        tree(branchlen - 15, 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()


if __name__ == '__main__':
    main()

另一个展现自相似性的分形是谢尔宾斯基三角形。 谢尔宾斯基三角形阐明了三路递归算法。用手绘制谢尔宾斯基三角形的过程很简单。 从一个大三角形开始。通过连接每一边的中点,将这个大三角形分成四个新的三角形。忽略刚刚创建的中间三角形,对三个小三角形中的每一个应用相同的过程。 每次创建一组新的三角形时,都会将此过程递归应用于三个较小的角三角形。 如果你有足够的铅笔,你可以无限重复这个过程。

因为我们可以无限地应用算法,什么是基本情况? 我们将看到,基本情况被任意设置为我们想要将三角形划分成块的次数。有时我们把这个数字称为分形的“度”。 每次我们进行递归调用时,我们从度中减去 1,直到 0。当我们达到 0 度时,我们停止递归。

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()


if __name__ == '__main__':
    main()

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值