Python基础:递归

Python基础:递归



一、 知识点详解

1.1 递归是什么?

  • 定义:递归是一种通过递归函数调用自身,实现将复杂问题分解为更小的同类子问题来解决的编程技巧。
    递归函数必须包含两个核心要素:
    1. 基线条件
    递归终止的条件,避免无限循环(如数值为0、空字符串、到达最底层节点等)。
    2. 递归条件
    函数调用自身的逻辑,每次调用必须缩小问题规模,向基线条件靠近。

1.2 为什么使用递归?

  • 简化代码
    递归能用简洁的代码解决复杂问题(如树遍历、分治算法)。
  • 自然映射问题
    适合处理具有自相似性的问题(如数学递推公式、文件系统层级结构)。
  • 分治思想
    将大问题分解为规模更小的同类子问题(如快速排序、斐波那契数列)。

1.3 怎么使用递归?

通过定义递归函数来实现复杂问题简单化

函数实现步骤
1. 定义基线条件
明确递归何时终止(如n=0n=1、空集合等)。
2. 缩小问题规模
每次递归调用必须修改参数,使其更接近基线条件(如n-1、子字符串、子列表等)。


1.4 案例说明

以上说明可能难于理解, 咱们用一个“拆盒子”小案例辅助理解 :

假如,你的朋友送给你一个礼物盒子,但是这个盒子比较有趣:
盒子里面装着一个更小的盒子,再里面又有一个更小的盒子……
直到最后一个盒子里装着一颗宝石。

递归就是“自己拆自己里面的盒子”

  1. 基线条件(停止条件)
    当拆到最后一个盒子(比如第5层)时,不再拆了,直接拿出宝石(这是递归的终点)。
  2. 递归条件(重复逻辑)
    每次拆开一个盒子,发现里面还有盒子,就继续拆里面的盒子(调用自己处理更小的问题)。

示例代码 :

def open_boxes(current_layer=1, max_layers=5):  
    '''
    --- 用于解释递归函数的拆盒子小案例 ---
    参数:  
        current_layer: 当前拆到的层数(默认从第1层开始)  
        max_layers: 最大层数(基线条件)  
    返回:  
        当拆到第5层时返回宝石信息,否则继续拆下一层盒子  
    '''  
    print(f"🔍正在拆第{current_layer}层盒子...".center(20, '='))  

    # 基线条件:拆到最大层数(第5层)时找到宝石  
    if current_layer == max_layers:  
        print('终于不是盒子了!')  
        return '✨ 里面有一颗宝石💎 ✨'  

    # 递归条件:打开后发现内层盒子,继续拆  
    print('打开后发现...还是一个盒子📦 !')  
    result = open_boxes(current_layer + 1)  # 递归调用,层数+1  
    return result  # 回归阶段:将结果逐层返回  

# 调用函数并打印结果  
print(open_boxes())  

输出结果 :

====🔍正在拆第1层盒子...====
打开后发现...还是一个盒子📦 !
====🔍正在拆第2层盒子...====
打开后发现...还是一个盒子📦 !
====🔍正在拆第3层盒子...====
打开后发现...还是一个盒子📦 !
====🔍正在拆第4层盒子...====
打开后发现...还是一个盒子📦 !
====🔍正在拆第5层盒子...====
终于不是盒子了!
✨ 里面有一颗宝石💎 ✨

执行流程 :

调用函数->
    递推-> 拆开第1层盒子...
      递推-> 拆开第2层盒子...
        递推-> 拆开第3层盒子...
          递推-> 拆开第4层盒子...
            递推-> 拆开第5层盒子...
            基线-> 找到宝石!
            (递推结束-> 开始回归)
          回归-> 第4层收到结果
        回归-> 第3层收到结果
      回归-> 第2层收到结果
    回归-> 第1层收到结果
返回结果-> 函数执行完毕

说明 :

  1. 递推阶段:从第1层逐层调用到第5层(每一层等待内层返回结果)。
  2. 回归阶段:第5层返回宝石,第4层接收后返回给第3层,直到第1层将结果输出。

1.5 递归的常见应用场景

  • 适合递归的核心条件:问题能分解为更小的同类子问题
    数学问题
    阶乘、斐波那契数列(第n项等于前两项之和)。
    简单重复任务
    比如计算1到n的和
    1+2+...+n = n + (1+2+...+n-1))。
    层级结构
    比如遍历文件夹(文件夹里有子文件夹,子文件夹里有文件,递归处理每个子文件夹)。

1.6 使用注意事项

  1. 基线条件必须存在
    缺少基线条件会导致无限递归,最终引发 RecursionError(Python默认递归深度限制约1000层)。
  2. 问题规模必须缩小
    每次调用自己,参数必须更接近基线(停止)条件(比如基线条件是n=0, 那么n应该每次减1,而不是加1)。
  3. 递归深度限制
    深度过深会触发递归深度限制错误,简单问题建议优先用循环(如阶乘、求和)。
  4. 性能问题
    递归代码简洁但存在函数调用开销,可能比循环效率低(如斐波那契数列递归实现存在大量重复计算)。

二、说明示例

场景1. 计算阶乘

def factorial(n):  
    if n == 0:  # 基线条件:0的阶乘为1  
        return 1  
    else:  
        # 递归条件:n的阶乘 = n × (n-1)的阶乘  
        return n * factorial(n - 1)  

print(factorial(5))  # 输出:120(5×4×3×2×1)              

场景2. 斐波那契数列

def fibonacci(n):  
    if n <= 1:   # 基线条件:F(0)=0,F(1)=1  
        return n  
    else:  
        # 递归条件:F(n) = F(n-1) + F(n-2)(n > 1时)  
        return fibonacci(n-1) + fibonacci(n-2)  

print(fibonacci(5))  # 输出:5(F(5) = F(4)+F(3)=3+2=5)  

场景3. 遍历目录结构

import os  

def list_files(path):  
    """
    递归遍历指定路径下的所有文件(不包含目录本身),并打印文件路径
    参数:
        path: 待遍历的目录路径(字符串)
    递归逻辑:
        1. 基线条件:遇到文件时直接打印路径
        2. 递归条件:遇到子目录时,递归调用自身处理该子目录
    """
    # 遍历当前路径下的所有条目(文件和子目录)
    for entry in os.listdir(path):  
        # 拼接完整路径(避免路径解析错误)
        full_path = os.path.join(path, entry)  
        
        # 检查是否为目录(递归条件:需要进一步处理的子问题)
        if os.path.isdir(full_path):  
            print(f"📁 进入子目录: {full_path}")  # 打印完整路径
            list_files(full_path)  # 递归处理子目录(问题规模缩小为子目录)
        
        # 基线条件:遇到文件时直接处理(不再继续分解问题)
        else:  
            print(f"📄 文件路径: {full_path}")  # 打印文件路径(递归终止点) 

三、知识点总结

  1. 递归定义
    函数自己调用自己,需包含基线条件(终止递归)和递归条件(缩小问题规模)。
  2. 使用原因
    用简洁代码解决自相似或分治问题(如树结构、数学递推)。
  3. 使用方法
    先定基线条件(如n=0),再通过参数变化(如n-1)逐步靠近基线。
  4. 应用场景
    问题能分解为更小同类子问题(如阶乘、斐波那契、目录遍历)。
  5. 注意事项
    必须有基线条件,参数需逐次靠近基线,避免深度超限(Python默认≈1000层),循环可能更高效。

四、扩展知识

4.1 递归 VS 递归函数

在 Python 中,递归和递归函数是紧密相连的概念,不过侧重点有所不同。下面咱们来看一下它们的具体差异:

  1. 递归
    递归是一种编程思想,其核心是在解决问题时,让问题的规模逐步缩小,最终归结到一个最基本的情况。
    它主要包含以下关键点:
    核心思路:把一个复杂的大问题分解成与原问题结构相同,但规模更小的子问题。
    持续进行这样的分解,直到子问题简单到无需再分解,也就是达到基线条件
    应用场景:递归思想适用于具有递归结构的问题,像阶乘计算、斐波那契数列问题等。
    实现方式:在 Python 中,递归这种思想通常借助递归函数来实现。

  2. 递归函数
    递归函数是实现递归思想的具体手段,它是一类特殊的函数,其特点如下:
    函数定义:在函数的定义中,函数会直接或间接地调用自身。
    关键组成
    1. 基线条件:这是递归终止的条件,若不设定,函数会陷入无限递归。
    2. 递归条件:在满足一定条件时,函数会调用自身来解决规模更小的子问题。

  3. 两个概念总结

    递归递归函数
    它是一种解决问题的思想和策略它是实现递归思想的具体编程工具
    强调的是问题分解和基线条件强调函数调用自身的实现方式
    是一种抽象的概念是具体的代码实现
  4. 递归函数执行流程图

调用函数
条件判断
基线条件
返回结果
递归条件
调用自身
函数执行完毕

五、知识点考察题

def func(n, r=0):
    if n == 0:
        return r
    else:
        r = n + func(n - 1)

print(func(2))

运行以上代码会出现以下哪种情况 ( )

  • A. 输出 : 0
  • B. 输出 : 3
  • C. 输出 : None
  • D. 报错TypeError

答案:D

解析
在递归条件(else分支)中,代码执行 r = n + func(n - 1)缺少 return r 语句,导致递归调用的返回值无法向上传递,函数隐式返回 None

  • n=2 时,递归调用 func(1),其内部同样因缺少返回语句返回 None
  • 最终执行 r = 2 + None,由于 intNone 无法相加,触发 TypeError(类型错误)。

关键点:递归函数必须确保每个分支都有显式的返回值,避免因隐式返回 None 导致逻辑错误。




关注「安于欣」获取更多Python技巧


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值