一、递归
递归Recursion是一种解决问题的方法
- 将问题分解为规模更小的相同问题,持续分解,直到问题规模小到可以用非常简单直接的方式来解决。
- 递归的问题分解方式非常独特,其算法方面的明显特征就是:在算法流程中调用自身。
- 递归为我们提供了一种对复杂问题的优雅解决方案,精妙的递归算法常会出奇简单。
重要:递归“三定律”
- 递归算法必须有一个基本结束条件(最小规模问题的直接解决)
- 递归算法必须能改变状态向基本结束条件演进(减小问题规模)
- 递归算法必须调用自身(解决减小了规模的相同问题)
示例:使用递归对数列进行求和
- 数列的和=“首个数”+“余下数列”的和
- 如果数列包含的数少到只有1个的话,它的和就是这个数了。这是规模小到可以做最简单的处理
- 问题分解为更小规模的相同问题,并表现为“调用自身”
- 对“最小规模”问题的解决:简单直接

def list_sum(num_list):
if len(num_list) == 1: # 基本结束条件
return num_list[0]
else:
# 减少规模,向长度为1的状态演进,并调用自身
return num_list[0] + list_sum(num_list[1:])
list1 = [1, 3, 5, 7, 9]
print(list_sum(list1))
### 输出结果
25
二、递归实现分析
当一个函数被调用的时候,系统会把调用时的现场数据压入到系统调用栈
- 每次调用,压入栈的现场数据称为栈帧
- 当函数返回时,要从调用栈的栈顶取得返回地址,恢复现场,弹出栈帧,按地址返回。

Python中的递归深度限制
- Python中的递归深度限制,默认最大递归深度为1000。超过最大递归深度会出现
RecursionError错误。 - 这时候要检查程序中是否忘记设置基本结束条件,导致无限递归。
import sys
print(sys.getrecursionlimit())
sys.setrecursionlimit(2000)
print(sys.getrecursionlimit())
### 输出结果
1000
2000
三、递归可视化-分形树
Python内置turtle海龟绘图模块模拟海龟在沙滩上爬行而留下的足迹
| 动作类别 | 方法名 | 参数 | 说明 |
|---|---|---|---|
| 爬行 | forward(n) | n | 向前爬行n个单位长度 |
| 爬行 | backward(n) | n | 向后爬行n个单位长度 |
| 转向 | left(a) | a | 向左转动a度 |
| 转向 | right(a) | a | 向右转动a度 |
| 抬笔放笔 | penup() | 无 | 抬起画笔,移动时不画线 |
| 抬笔放笔 | pendown() | 无 | 放下画笔,移动时开始画线 |
| 笔属性 | pensize(s) | s | 设置画笔线条的粗细为s |
| 笔属性 | pencolor© | c | 设置画笔的颜色为c |
示例:画正方形
import turtle
t = turtle.Turtle()
for i in range(4):
t.forward(100)
t.right(90)
turtle.done()
示例:画五角星
import turtle
t = turtle.Turtle()
t.pencolor("red")
t.pensize(3)
for i in range(5):
t.forward(200)
t.right(144)
turtle.done()
示例:递归画螺旋
from turtle import Turtle
import turtle
t = turtle.Turtle()
def draw_spiral(t: Turtle, line):
if line > 0:
t.forward(line)
t.right(90)
draw_spiral(t, line - 5)
draw_spiral(t, 100)
turtle.done()

分形树:自相似递归图形
- 分形Fractal,是1975年由Mandelbrot开创的新学科。“一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状”,即具有自相似的性质。
- 分形是在不同尺度上都具有相似性的事物。我们能看出一棵树的每个分叉和每条树枝,实际上都具有整棵树的外形特征(也是逐步分叉的)
- 我们可以把树分解为三个部分:树干、左边的小树、右边的小树。分解后,正好符合递归的定义:对自身的调用

from turtle import Turtle
import turtle
t = turtle.Turtle()
t.left(90)
t.pencolor("green")
def draw_tree(t: Turtle, line):
if line > 0: # 递归结束条件
t.forward(line) # 画树干
t.right(30) # 右倾30度
draw_tree(t, line - 15) # 递归调用,画右边小树
t.left(60) # 向左回60度,即左倾30度
draw_tree(t, line - 15) # 递归调用,画左边小树
t.right(30) # 向右回30度,即回正
t.backward(line) # 退回原位置
draw_tree(t, 100)
turtle.done()

四、递归可视化-谢尔宾斯基三角形
谢尔宾斯基Sierpinski三角形
- 分形构造,平面称谢尔宾斯基三角形,立体称谢尔宾斯基金字塔
- 真正的谢尔宾斯基三角形是完全不可见的,其面积为0,但周长无穷,是介于一维和二维之间的分数维(约1.585维)构造
- 根据自相似特性,谢尔宾斯基三角形是由3个尺寸减半的谢尔宾斯基三角形按照品字形拼叠而成
算法分析
- 在degree有限的情况下,degree=n的三角形,是由3个degree=n-1的三角形按照品字形拼叠而成
- 同时,这3个degree=n-1的三角形边长均为degree=n的三角形的一半(规模减小)
- 当degree=0,则就是一个等边三角形,这是递归基本结束条件

import turtle
def draw_triangle(t: turtle.Turtle, points: dict, color: str):
# 绘制等边三角形
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 get_mid(p1, p2):
# 取两点的中点
return (p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2
def draw_sierpinski(degree, points):
# 递归画谢尔宾斯基三角形
color = "black" if degree % 2 != 0 else "white"
draw_triangle(t, points, color)
if degree > 0: # 最小规模,0直接退出
# 左三角形
draw_sierpinski(
degree - 1,
{
"left": points["left"],
"top": get_mid(points["left"], points["top"]),
"right": get_mid(points["left"], points["right"]),
},
)
# 上三角形
draw_sierpinski(
degree - 1,
{
"left": get_mid(points["left"], points["top"]),
"top": points["top"],
"right": get_mid(points["top"], points["right"]),
},
)
# 右三角形
draw_sierpinski(
degree - 1,
{
"left": get_mid(points["left"], points["right"]),
"top": get_mid(points["right"], points["top"]),
"right": points["right"],
},
)
t = turtle.Turtle()
points = {"top": (0, 200), "left": (-200, -100), "right": (200, -100)}
draw_sierpinski(degree=3, points=points)
turtle.done()
当degree为3时的三角形

您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

491

被折叠的 条评论
为什么被折叠?



