python数据结构与算法学习之递归

递归Recursion

什么是递归

  • 递归(recursion)是一种解决问题的方法,其精髓在于将问题分解为规模更小的相同问题,持续分解直到问题规模小到可以用非常简单直接的方式来解决。
  • 递归的问题分解方式非常独特,其算法方面的明显特征就是在算法流程中调用自身

递归三定律:

  • 递归算法必须有一个基本结束条件(最小规模问题的直接解决)
  • 递归算法必须能改变状态向基本结束条件演进(减小问题规模)
  • 递归算法必须调用自身(解决减小了规模的相同问题)

递归“三定律”:数列求和问题

  • 数列求和问题首先具备了基本结束条件:当列表长度为1的时候,直接输出所包含的唯一数
  • 数列求和处理的数据对象是一个列表,而基本结束条件是长度为1的列表,那递归算法就要改变列表并向长度为1的状态演进我们看到其具体做法是将列表长度减少1。
  • 调用自身是递归算法中最难理解的部分,实际上我们理解为“问题分解成了规模更小的相同问题”就可以了在数列求和算法中就是“更短数列的求和问题”
# 递归版数列求和:
def listsum(numList):
    if len(numList) == 1:
        return numList[0]
    else:
        return numList[0] + listsum(numList[1:])

print(listsum([1,3,5,7,9]))

请添加图片描述

递归的应用:任意进制转换

  • 假设是要转换为十进制,比十小的整数转换为十进制,直接查表就可以得到,比十大的整数就想办法拆成一系列比十小的整数,逐个查表。
  • 所以递归基本结束条件就是小于十的整数,拆解整数的过程就是向基本结束条件演进的过程
  • 拆解过程分为两步:1. 除以“进制基base” (// base);2. 对“进制基”求余数(% base)。
  • 此时问题就分解为:1. 余数总小于“进制基base”,是基本结束条件,可直接查表转换;2. 整数商成为更小规模问题,通过递归调用自身解决。
    请添加图片描述
# 递归版整数转换为任意进制:
def toStr(n,base): #  n为整数base为任意进制
    convertString = "0123456789ABCDEF"
    # print(convertString[5])
    if n < base:  
        return convertString[n]  # 最小规模
    else:
        return toStr(n//base,base) + convertString[n%base]  # 整除减小规模,调用自身

print(toStr(1453,16))

递归调用的实现:递归深度的限制

  • 当一个函数被调用的时候,系统会把调用时的现场数据压入到系统调用栈
  • 每次调用,压入栈的现场数据成为栈帧,当函数返回时,要从调用栈的栈顶取得返回地址,恢复现场,弹出栈帧,按地址返回。
    请添加图片描述
  • 在Python内置的sys模块可以获取和调整最大递归深度(不然可能会出现错误:RecursionError ,因为容量有限或者未设置基本结束条件导致无限递归)
import sys
sys.getrecursionlimit()   # 返回1000
sys.setrecursionlimit(3000)
sys.getrecursionlimit()   # 返回3000

递归的可视化:分形树

  • Python的海龟做图系统turtle module是Python内置的,随时可用,以LOGO语言的创意为基础,其意象为模拟海龟在沙滩上爬行而留下的足迹。
  1. 爬行:forward(n); backward(n)

  2. 转向:left(a); right(a)

  3. 抬笔放笔:penup(); pendown()

  4. 笔属性:pensize(s); pencolor©

import turtle

# 正方形:
t = turtle.Turtle()
for i in range(4):
    t.towards(100)
    t.right(90)
turtle.done()

# 五角星:
t = turtle.Turtle()
t.pencolor("red")
t.pensize(3)
for i in range(5):
    t.towards(100)
    t.right(144)
t.hideturtle()
turtle.done()

# 螺旋线:
t = turtle.Turtle()
def drawSpiral(t,lineLen):
    if lineLen > 0:
        t.forward(lineLen)
        t.right(90)
        drawSpiral(t,lineLen-5)
drawSpiral(t,200)
turtle.done()

螺旋线螺旋线

  • 分形树:自相似递归图形
  1. 分形Fractal,是1975年由Mandelbrot开创的新学科。“一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状”,即具有自相似的性质。
  2. 分形是在不同尺度上都具有相似性的事物。
import turtle
​
def tree(branch_len):
    if branch_len > 5:   # 树干太短不画,即递归结束条件
        t.forward(branch_len)  # 画树干
        t.right(20)  # 向右倾斜
        tree(branch_len - 5)  # 递归调用,画右边的小树
        t.left(40)  # 向左回40°,向左倾斜
        tree(branch_len - 5)  # 递归调用,画左边的小树
        t.right(20)  # 向右回20°,即回正
        t.backward(branch_len)  # 海归退回原位置
        
t = turtle.Turtle()
t.left(90)
t.penup()
t.backward(100)
t.pendown()
t.pencolor('green')
t.pensize(2)
tree(75)  # 画树干长度为75的二叉树
t.hideturtle()
turtle.done()

分形树在这里插入图片描述

递归的可视化:谢尔宾斯基三角形

  • 分形构造,平面称谢尔宾斯基三角形,立体称谢尔宾斯基金字塔。实际上,真正的谢尔宾斯基三角形是完全不可见的,其面积为0,但周长无穷,是介于一维和二维之间的分数维(约1.585维)构造。
  • 根据自相似特性,谢尔宾斯基三角形是由3个尺寸减半的谢尔宾斯基三角形按照品字形拼叠而成。由于我们无法真正做出谢尔宾斯基三角形(degree —> ∞),只能做degree有限的近似图形。
    请添加图片描述
  • 在degree有限的情况下,degree=n的三角形,是由3个degree=n-1的三角形按照品字形拼叠而成。同时,这3个degree=n-1的三角形边长均为degree=n的三角形的一半(规模减小)。当degree=0,则就是一个等边三角形,这是递归基本结束条件。
import turtle
def sierpinski(degree,points):
    colormap = ['blue','red','green','yellow','orange','pink']
    # 绘制等边三角形
    drawTriangle(points,colormap[degree])
    # 最小规模,0直接退出
    if degree > 0:
        # 减小规模,getMid边长减半,调用自身,左上右的次序
        sierpinski(degree-1,{
   'left':points['left'],
                            'top':getMid(points['left'],points['top']),
                            'right':getMid(points['left'],points['right'])})
        sierpinski(degree-1,{
   'left':getMid(points['left'],points['top']),
                            'top':points['top'],
                            'right':getMid(points['top'],points['right'])})
        sierpinski(degree-1,{
   'left':getMid(points['left'],points['right'])
                            'top':getMid(points['right'],points['top'])
                            'right':points['right']})
        
def drawTriangle(points,color):
    t.fillcolor(color)
    t.penup()
    t.goto(points['top'])
    t.pendown()
    t.begin_fill()
    t.goto(points['left'])
    t.goto(points['right'])
    t.goto(points['top'])
    t.end_fill()
    
def getMid(p1,p2):
    return((p1[0]+p2[0])/2,(p1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值