递归案例
入门
概述
在Python中, 我们把 函数自己调用自己的情况, 称之为: 递归.
注意事项:
-
递归必须要有出口, 否则容易造成: 死递归.
-
递归调用次数不能过多, 否则容易造成: 死递归.
-
搞定递归很简单, 只要分析清楚 出口 和 规律即可.
示例代码
""" 递归简介: 概述: 在Python中, 我们把 函数自己调用自己的情况, 称之为: 递归. 注意事项: 1. 递归必须要有出口, 否则容易造成: 死递归. 2. 递归调用次数不能过多, 否则容易造成: 死递归. 3. 搞定递归很简单, 只要分析清楚 出口 和 规律即可. """ # 案例: 递归代码演示 # 1. 定义fun01()函数, 实现自己调用自己. # 计数器 count = 0 def fun01(): # 设置修改全局变量的值. global count count += 1 # 正常打印 print(f'我是 fun01 函数, {count}') # 出口 if count >= 100: return # 函数自己调用自己 => 递归. fun01() # 2. 在main方法中测试. if __name__ == '__main__': # 调用 fun01()函数 fun01()
阶乘
需求
""" 需求: 计算5的阶乘. 阶乘公式: 5! = 5 * 4 * 3 * 2 * 1 = 5 * 4! = 120 即: n! = n * (n - 1)! 求5的阶乘, 思路分析: 5! = 5 * 4 * 3 * 2 * 1 ------- 5 * 4! | 4 * 3! 规律 3 * 2! | 2 * 1! ------- 1! = 1 出口 """
代码
# 1. 定义函数 factorial(n), 用来计算 n的阶乘. def factorial(n): # 出口, 1! = 1 if n == 1: return 1 # 规律 return n * factorial(n - 1) # 2. 在main方法中测试. if __name__ == '__main__': result = factorial(5) print(f'result: {result}') # 120
图解
不死神兔(斐波那契)
需求
案例: 不死神兔, 斐波那契数列.
介绍: 传说在很久很久以前, 有一个意大利青年, 叫: 斐波那契, 有一天, 他提出来一个非常有意思的问题: 1. 假设1对小兔子, 一个月之后会长成1对大兔子. 2. 每对大兔子, 每个月都会生1对小兔子. 3. 问: 假设所有的兔子都不死的情况下, 1对小兔子, 1年(即: 12个月)之后会变成多少对兔子?
分析流程: 月份 大兔子对数 小兔子对数 当月兔子总对数 1月 0 1 1 2月 1 0 1 3月 1 1 2 4月 2 1 3 5月 3 2 5 6月 5 3 8 ...... 12月 * * 144
大白话解释斐波那契数列: 已知数列: 1, 1, 2, 3, 5, 8, 13, 21... 问: 第12个整数是多少?
结论: 出口: 前两个月(第1月, 第2月)的兔子对数 = 1, 即: m = 1 or m = 2时, 兔子对数 = 1 规律: 从第3个月开始, 每月兔子对数 = 上月兔子对数 + 上上月兔子对数, 即: m月兔子对数 = (m-1)月兔子对数 + (m-2)月兔子对数
代码
# 1. 定义函数, 计算 month 月的兔子对数. def get_rabbit(m): # 出口, 前两个月的兔子对数 = 1 # if m == 1 or m == 2: # if m <= 2: if m in [1, 2]: return 1 # 兔子对数 # 规律: m月兔子对数 = (m-1)月兔子对数 + (m-2)月兔子对数 return get_rabbit(m - 1) + get_rabbit(m - 2) # 2. 在main函数中调用 if __name__ == '__main__': # 思路1: 递归版. num = get_rabbit(12) print(f'第12个月的兔子对数为: {num} 对!') print('-' * 30) # 思路2: 用 列表 实现, 已知列表 rabbit_list = [1, 1], 后续的每个值 = 前两个值之和, 问: 列表的第12个值是多少? # 1. 定义列表, 记录 每月的兔子对数. rabbit_list = [] # 2. 因为前两个月的兔子对数都是1, 我们手动添加即可. rabbit_list.append(1) rabbit_list.append(1) # 3. 从第3个值(即: 第3月开始)有规律了, 当月 = 前两个值之和. for i in range(2, 12): # i(索引)的值: 2 ~ 12, 包左不包右, 即: 2 ~ 11 分别表示 3月 ~ 12月 # 4. 规律: 当前值 = 前两个值之和. rabbit_list.append(rabbit_list[i - 1] + rabbit_list[i - 2]) # 5. 打印结果. print(rabbit_list) print(f'第12个月的兔子对数为: {rabbit_list[11]} 对!') print(f'第12个月的兔子对数为: {rabbit_list[len(rabbit_list) - 1]} 对!')
爬楼梯
需求
需求: 假设需要爬n阶台阶才能到达顶层, 每次可以选择爬1阶或者2阶. 问: 共有几种方式可以到达顶层.例如: 到达顶层的楼梯总台阶数 你可以选择的具体的爬楼梯的方案 方案总数 1 1 1 2 11,2 2 3 21,12,111 3 4 1111,112,121,211,22 5 5 11111,1112,1121,1211,2111,122,212,221 8 ......结论: 出口: n=1 => 1 n=2 => 2 规律: n >= 3, n = (n - 1)的方案总数 + (n - 2)的方案总数
代码
# 1. 定义函数, 递归实现 计算爬楼梯总方案数.' def climb_stairs(n): # 出口 if n == 1: return 1 # 1个台阶, (爬楼梯)总方案数 = 1 elif n == 2: return 2 # 2个台阶, (爬楼梯)总方案数 = 2 # 规律 return climb_stairs(n - 1) + climb_stairs(n - 2) # 2. 在main函数中测试. if __name__ == '__main__': # 思路1: 递归版. count = climb_stairs(6) print(f'爬楼梯总方案数为: {count}') # 思路2: for循环 + 列表版 实现. # 列表爬楼梯 steps = [] steps.append(1) steps.append(2) for i in range(2, 11): # 当前楼梯的方案数 = 前面一级楼梯方案数 + 前面的前面一级的方案数 steps.append(steps[-1] + steps[-2]) print(steps[-1])
打印文件夹路径
需求
键盘录入1个文件夹路径, 打印该文件夹下所有文件(包括子文件夹)
出口: 如果是文件, 就打印, 不递归.规律: 如果是文件夹, 就递归.
代码
import os # Operating System, 系统模块, 里边定义的主要是和 文件, 文件夹(路径)相关的操作. # 1. 定义函数 print_dir(path), 打印 path 文件夹路径下所有的子级. def print_dir(path): """ 自定义函数, 打印path路径下 所有的子级, 包括子级的子级 :param path: 要被打印的 文件夹路径 :return: 无 """ # 1.1 判断path是否是 存在的 文件夹路径. if os.path.isdir(path): # 假设: path = 'd:/src' # 1.2 走到这里, 说明是 存在的 文件夹路径, 就: 获取其所有的子级. child_names = os.listdir(path) # ['aa', 'ai30', '代码打字练习词库.txt', '面试题.py'] # 1.3 把上述的 子级文件(夹)的名字, 拼接成: 完整的 子级路径. for child_name in child_names: # 1.4 具体的拼接子级路径的动作, 即: 子级路径 = path + '/' + child_name child_path = path + '/' + child_name # 1.5 打印 子级路径 print(child_path) # 1.6 判断当前的子级路径是否是 文件夹路径, 如果是文件夹, 就: 递归. if os.path.isdir(child_path): # 递归 print_dir(child_path) else: # 1.3 走到这里, 说明路径非法. print('路径有误, 不是文件夹路径 或者 路径不存在, 程序结束!') # 2. 在main方法中, 测试. if __name__ == '__main__': print_dir('d:/src')
os模块常用函数
# 创建目录 os.mkdir('d:/aaa') # 改名 os.rename('d:/aaa', 'd:/xyz') # 删除目录, 必须是: 空目录 os.rmdir('d:/dest') # 查看目录下, 所有的子级(不包括子级的子级), 只能获取: 子级的名字, 而不是子级的目录. list_dir = os.listdir('d:/src') print(type(list_dir)) # <class 'list'> print(list_dir) # ['aa', 'ai30', '代码打字练习词库.txt', '面试题.py'] # os.path.isdir(路径): 判断是否是 存在的 文件夹. print(os.path.isdir('d:/src/aa')) # 存在, 且是文件夹. True print(os.path.isdir('d:/src/bb')) # 不存在. False print(os.path.isdir('d:/src/面试题.py')) # 存在的, 但是是文件. False print('-' * 30) # os.path.isfile(路径): 判断是否是 存在的 文件 print(os.path.isfile('d:/src/面试题.py')) # True, 存在, 且是文件. print(os.path.isfile('d:/src/abc.py')) # False, 不存在, 是文件. print(os.path.isfile('d:/src/aa')) # False, 存在, 是文件夹. print('-' * 30) # os.path.exists(路径): 判断是否是 存在的文件 或者文件夹. print(os.path.exists('d:/src/面试题.py')) # True, 存在的 文件 print(os.path.exists('d:/src/aa')) # True, 存在的 文件夹. print(os.path.exists('d:/src/bb')) # False, 不存在的. print('-' * 30) # os.path.basename(路径): print(os.path.basename('d:/src/面试题.py')) # 面试题.py print(os.path.basename('d:/src/1.py')) # 1.py print(os.path.basename('d:/src/aa')) # aa print(os.path.basename('d:/src/bb')) # bb print(os.path.basename('d:/ai30/base_class/hg/xyz')) # xyz