基于python的数据结构和算法(北京大学)第五章(递归)

此章理解较为困难,未写自写代码,仅仅记录标准代码并复现。

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

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

  • 初识递归:数列求和
    算法思路:数列的和 = 首个数 + 余下数列的和 ,如果数列包含的数少到只有1个的话,它的和就是这个数了。
    代码实现:

def  ListSum(alist):
	if  len(alist) == 1:
		return alist[0]
	else:
		return alist[0] + ListSum(alist[1 : ])
print(ListSum([1,3,5,7,9]))

在这里插入图片描述
根据递归的“三定律”来分析下数列求和问题:
1.数列求和问题首先具备了基本结束条件:当列表长度为1的时候,直接输出所包含的唯一数。
2.数列求和处理的数据对象是一个列表,而基本结束条件是长度为1的列表,那递归算法就要改变列表并向长度为1的状态演进。(具体做法为将列表长度减1)
3.调用自身:在数列求和算法中就是”更短数列的求和问题“

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

注意点为 return toStr(n//base,base) + constring(n % base)此语句,因为取余数之后要”倒“写结果(最先得到的余数放在最后面)所以根据递归过程,要把进入递归的”入口“写在得到余数的前面。

  • 递归调用的实现:
    在这里插入图片描述
    在python中递归深度有限制:
def tell_story():
	print("从前有座山,山上有座庙,庙里有个老和尚,他在讲:”)
	tell_story()
print("给你讲个故事")
tell_story()

在这里插入图片描述

这时候要检查程序中是否忘记设计基本结束条件,导致无限递归。或者向基本技术条件的演进太慢,导致递归层数太多,调用栈溢出。

  • python中的递归深度限制:
    在这里插入图片描述

  • 递归的可视化:图示
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    代码实现:

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

  • 分形树:自相似递归图形问题:
    分形Fractal,是1975年由Mandelbrot开创的新学科。“一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状”,即具有自相似的性质。
    分形是在不同尺度上都具有相似性的事物。
    在这里插入图片描述
    代码:
# 分形树:
def tree(branch_len):
    if branch_len > 5: # 树干太短不画,即递归结束条件
        t.forward(branch_len)  # 画树干
        t.right(20)  # 向右倾斜20度
        tree(branch_len - 15)  # 递归调用,画右边的小树,树干减15
        t.left(40)  # 向左回40度,即左倾斜20度
        tree(branch_len - 15)  # 递归调用,画左边的小树,树干减15
        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()

  • 谢尔宾斯基Sierpinski三角形:
    分形构造,平面称谢尔宾斯基三角形,立体称谢尔宾斯基金字塔。实际上,真正的谢尔宾斯基三角形是完全不可见的,其面积为0,但周长无穷,是介于一维和二维之间的分数维(约1.585维)构造。
    根据自相似特性,谢尔宾斯基三角形是由3个尺寸减半的谢尔宾斯基三角形按照品字形拼叠而成。由于我们无法真正做出谢尔宾斯基三角形(degree —> ∞),只能做degree有限的近似图形。
    在这里插入图片描述
    在degree有限的情况下,degree=n的三角形,是由3个degree=n-1的三角形按照品字形拼叠而成。同时,这3个degree=n-1的三角形边长均为degree=n的三角形的一半(规模减小)。当degree=0,则就是一个等边三角形,这是递归基本结束条件

代码参考一个更好理解的代码:

#!/usr/bin/python3
import turtle


t = turtle.Turtle()


def get_midpoint(a, b):
    ax, ay = a
    bx, by = b
    return (ax + bx) / 2, (ay + by) / 2


def draw_triangle(a, b, c):
    ax, ay = a
    bx, by = b
    cx, cy = c

    t.penup()
    t.goto(ax, ay)
    t.pendown()
    t.goto(bx, by)
    t.goto(cx, cy)
    t.goto(ax, ay)
    t.penup()


def draw_sierpinski(triangle, depth):
    """
    :param triangle: 指定三角形三个顶点坐标,示例:((ax,ay),(bx,by),(cx,cy))。
    :param depth: 指定层数
    """
    a, b, c = triangle
    draw_triangle(a, b, c)
    if depth == 0:
        return
    else:
        d = get_midpoint(a, b)
        e = get_midpoint(b, c)
        f = get_midpoint(c, a)
        draw_sierpinski([a, d, f], depth-1)
        draw_sierpinski([d, b, e], depth-1)
        draw_sierpinski([f, e, c], depth-1)


if __name__ == '__main__':
    triangle = [[-200, -100], [0, 200], [200, -100]]
    draw_sierpinski(triangle, 5)
    turtle.done()

原课程代码为:

import turtle

def sierpinski(degree,points):
    colormap = ['blue','red','green','white','yellow','orange']
    drawTriangle(points,colormap[degree])
    if degree>0:
        sierpinski(degree-1,
                   {'left':points['left'],
                    'right':getMid(points['left'],points['right']),
                    'top':getMid(points['top'],points['left'])})
        sierpinski(degree-1,
                   {'left':getMid(points['top'],points['left']),
                    'right':getMid(points['left'],points['right']),
                    'top':points['top']})
        sierpinski(degree - 1,
                   {'left': getMid(points['top'], points['left']),
                    'right': points['right'],
                    'top': getMid(points['top'], 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[1]+p2[1])/2)

t = turtle.Turtle()
points = {'left':(-200,-100),
          'top':(0,200),
          'right':(200,-100)}

sierpinski(5,points)
turtle.done()

  • 汉诺塔问题:
def moveTower(height,fromPole,withPole,toPole):
    if height>=1:
        moveTower(height-1,fromPole,toPole,withPole)
        moveDisk(fromPole,toPole)
        moveTower(height-1,withPole,fromPole,toPole)

def moveDisk(disk,fromPole,toPole):
    print(f"moving disk[{disk}] from {fromPole} to {toPole}")

moveTower(3,"#1","#2","#3")

参考博文:
https://blog.csdn.net/python1639er/article/details/104038495?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159471622819725219913743%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=159471622819725219913743&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v3~pc_rank_v2-4-104038495.first_rank_ecpm_v3_pc_rank_v2&utm_term=python%E8%B0%A2%E5%B0%94%E5%AE%BE%E6%96%AF%E5%9F%BA

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值