1. 什么是递归
递归实际上函数的递归,而函数递归就是函数嵌套调用的一种特殊方式
具体是指:在调用一个函数的过程中又直接或间接的调用了函数本身
1.1 直接调用
举例 : 在调用func1的过程中,又调用了func1,这就是直接调用函数本身
def func1():
print("我是func1====>")
func1()
func1()
# 首先调用func1,然后函数体内部代码再调用func1,虽然局部作用域中没有func1这个名字,但是可以从全局
# 作用域获得
1.2 间接调用
举例 : 在调用func1的过程中,又调用了func2,func2中又调用func1 , 这就是间接调用
def func1():
print("我是func1====>")
func2()
def func2():
print("我是func2====>")
func1()
func1()
递归就是在自己调用自己,实际上递归的本质就是循环
2. 一段代码的循环执行
之前我们讲过循环的两种方式,一种while循环,一种for循环,现在又多了一种方式,就是递归
2.1 方式一
while True:
print(1111)
print(2222)
print(3333)
# 在循环体中放入要循环的代码
2.2 方式二
def func():
print(1111)
print(2222)
print(3333) # 函数体中写入要循环执行的代码
func()
func()
2.3 注意
递归可以实现的while True都可以实现
递归不能无限循环下去,因为递归是在调用函数,你调用函数就要创建局部名称空间用来存放名字,如果你一直递
归,也就是要一直创建名称空间,会消耗大量的内存,如果不加以限制,会造成内存溢出。所以应该在满足某种
条件下,结束递归,即结束函数的调用。
#1. 可以使用sys.getrecursionlimit()去查看递归深度,默认值为1000,虽然可以使用
# sys.setrecursionlimit()去设定该值,但仍受限于主机操作系统栈大小的限制
#2. python不是一门函数式编程语言,无法对递归进行尾递归优化。
#尾递归优化就是在调用下一次自己的时候,把上一次调用产生的名称空间回收了,但是python做不到
3. 回溯和递推
我们将用一个小学算年龄的问题来解释递归的原理和使用
# 问题:
有五个人,问第5个人他说我比第4个人大5岁,问第4个人他说我比第3个人大5岁,问第3个人他说我比第2个人大5岁,问第2个人他说我比第1个人大5岁,问第1个人他说他10岁,问1,2,3,4,5各几岁?
伪代码:
age(5) = age(4) + 5
age(4) = age(3) + 5
age(3) = age(2) + 5
age(2) = age(1) + 5
age(1) = 10
总结:age(n) = age(n-1) + 5
在回溯阶段,要求第n个人的年龄,需要回溯得到(n-1)个人的年龄,以此类推,直到得到第一个人的年龄,此
时,age(1)已知,因而不必再向前回溯了。然后进入递推阶段:从第一个人的年龄可以推算出第二个人的年龄
15,从第二个人的年龄可以推算出第三个人的年龄20,以此类推,一直推算出第五个人的年龄30为止,递归结
束。需要注意的一点是,递归一定要有一个结束条件,这里n=1就是结束条件。
def age(n):
if n == 1:
return 10
return age(n-1) + 5
# return age(1) + 5 + 5 + 5 + 5
递归的应用场景
当你遇到一种需求中 , 有功能是需要简单的重复实现的话 , 就可以把这这个功能定义一个函数 , 然后在内部自己调
用自己 , 实现功能的重复
4. 小练习
使用递归迭代打印出列表中的所有值
l = [1, 2, 3, [1, 2, [1, 2, [4, 5, 6, [7, 8, [10, 11, 12, [13, 14, 15],7,8,9],18,20],21]]]]
def func(l):
for i in l:
if type(i) == list:
l = i
func(l)
else:
print(i)
func(l)
5. 二分法
二分法是一种算法,所谓算法就是一种高效解决问题的方法,二分法常用来快速查找数据
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 999, 11, 12, 13, 14]
def binary_search(find_num, l):
print(l)
if not len(l):
print('要找的值不存在')
return
mid_val_index = len(l) // 2
mid_val = l[mid_val_index] # 当列表为空,再用索引取值就报错
if find_num > mid_val:
# 接下来的查找应该是在列表的右半部分
l = l[mid_val_index + 1:] # 切片超出索引会得到有个空列表
binary_search(find_num, l)
elif find_num < mid_val:
# 接下来的查找应该是在列表的左半部分
l = l[:mid_val_index]
binary_search(find_num, l)
else:
print('find it')
binary_search(999, lst)
# 如果你遍历要找9次,但是二分法,3次就找到了