您的位置:首页 > 理论基础 > 数据结构算法

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

2020-07-14 17:33 344 查看

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

  • 什么是递归(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

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: