一、递归
1.递归的定义
递归是函数嵌套调用中的一种特殊形式,函数在定义时,间接或直接调用自身,叫递归调用。
#直接调用本身
def f1():
print('============>递归')
f1()
func()
# #间接调用本身
def f1():
print('=============>我是f1')
f2()
def f2():
print('=============>我是f2')
f1()
f2()
# 直接调用或者间接调用本身,形成死循环,这种递归是无意义的递归,超过python限制的调用层级,代码会报错。
RecursionError: maximum recursion depth exceeded while calling a Python object
调用函数会产生局部的名称空间,占用内存,因为上述这种调用会无需调用本身,python解释器的内存管理机制为了防止其无限制占用内存,对函数的递归调用做了最大的层级限制。
2. 修改递归调用最大深度
import sys
sys.getrecursionlimit()
sys.setrecursionlimit(2000) #将默认的1000修改为2000
def f1(n):
print('from f1',n)
f1(n+1)
f1(1)
虽然可以设置 递归(循环)的最大深度,但是每递归一次,对应的函数名和值都会储存在内存中,而且无限制地递归调用本身是毫无意义的,递归应该分为两个明确的阶段,回溯与递推。
3.递归的特点
- 特点1:递推
- 特点2:回溯
由上图可知:
回溯:就是从外向内一层一层递归调用下去,回溯阶段必须要有一个明确的结束条件,每进入下一次递归,问题的规模都应该有所减少。
递推:就是获取那个最总结束条件,并一层层的结束递归。
#递归小练习,取出下方列表的值
lists = [1, [3, [5, [89, [67, [23,]]]]]]
def func(lists):
for item in lists:
if type(item) is list:
func(item)
else:
print(item)
res = func(lists)
print(res)
4.递归总结
-
必须有一个明确的结束条件
-
每进入深一层级递归时,问题规模要比上次有所减少(单纯调用自身是无意义的)
-
递归效率不高,递归层级过多会导致栈溢出。
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出
二、二分法
想从一个按照从小到大排列的数字列表中找到指定的数字,遍历的效率太低,用二分法(算法的一种,算法是解决问题的方法)可以极大低缩小问题规模
l=[1,2,10,30,33,99,101,200,301,311,402,403,500,900,1000] #从小到大排列的数字列表
def search(n,l):
print(l)
if len(l) == 0:
print('not exists')
return
mid_index=len(l) // 2
if n > l[mid_index]:
#in the right
l=l[mid_index+1:]
search(n,l)
elif n < l[mid_index]:
#in the left
l=l[:mid_index]
search(n,l)
else:
print('find it')
search(3,l)
## 实现类似于in的效果
对于一个有序的序列可以使用二分法提高查找效率
原理: 将整个序列分为两半然后判断要找的数据是在左边还是右边从而排除一半的数据