Python中的递归详解

简介: Python中的递归详解

1.递归函数

工作中,我们可能经常会遇到递归函数的使用,今天我们就来深入讲讲什么是递归。

递归函数 : 在函数内部,可以调用其他函数。如果一个函数在内部自己调用自己,这个函数就是递归函数。必须由出口

递 : 去

归 : 回

一去一回叫做递归

def digui(n):
    print(n,"<==1==>")
    if n > 0:
        digui(n-1)
    print(n,"<==2==>")

digui(5)

#此递归函数开辟了6层空间。去的过程

n = 5 print(5,"<==1==>")  if 5 > 0:   digui(5-1) =>  digui(4) 代码阻塞在第12行,下面的代码不再执行
n = 4 print(4,"<==1==>")  if 4 > 0:   digui(4-1) =>  digui(3) 代码阻塞在第12行
n = 3 print(3,"<==1==>")  if 3 > 0:   digui(3-1) =>  digui(2) 代码阻塞在第12行
n = 2 print(2,"<==1==>")  if 2 > 0:   digui(2-1) =>  digui(1) 代码阻塞在第12行
n = 1 print(1,"<==1==>")  if 1 > 0:   digui(1-1) =>  digui(0) 代码阻塞在第12行
n = 0 print(0,"<==1==>")  if 0 > 0: 不成立 print(0,"<==2==>") 到此最后一层函数空间彻底执行完毕   这是在一个空间里面打印的

# 回的过程,从阻塞的地方代码往下走
回到上一层函数空间  n = 1 代码在第12行的位置,继续往下执行  print(1,"<==2==>")
回到上一层函数空间  n = 2 代码在第12行的位置,继续往下执行  print(2,"<==2==>")
回到上一层函数空间  n = 3 代码在第12行的位置,继续往下执行  print(3,"<==2==>")
回到上一层函数空间  n = 4 代码在第12行的位置,继续往下执行  print(4,"<==2==>")
回到上一层函数空间  n = 5 代码在第12行的位置,继续往下执行  print(5,"<==2==>")

到此递归函数执行结束…

打印 543210012345

调用函数就得开辟空间,每次开辟的栈帧空间,代码必须全部执行完毕之后才释放空间,再回到上一个栈帧执行没结束的代码

2.递归流程图

每次调用函数时,都要单独在内存当中开辟空间,叫做栈帧空间,以运行函数中的代码

3.递归总结

(1)递归实际上是不停的开辟栈帧空间和释放栈帧空间的过程,开辟就是去的过程,释放就是回的过程
(2)递归什么时候触发归的过程:
    1.当最后一层栈帧空间执行结束的时候,触发归的过程.
    2.当遇到return返回值的时候终止当前函数,触发归的过程.
(3)递归不能无限的去开辟空间,可能造成内存溢出,蓝屏死机的情况, 所以一定要给予跳出的条件(如果递归的层数太大,不推荐使用)
(4)开辟的一个个栈帧空间,数据是彼此独立不共享的.

递归不能不限开辟空间

官方说法最大默认是1000层. 但实际要看具体设备硬件性能

def deepfunc():

deepfunc()

deepfunc()

可以通过sys.setrecursionlimit()进行设置,但是一般默认不会超过3925-3929这个范围。

我这台电脑最大递归深度996层

4.递归注意事项

函数调用的过程就是开辟栈帧和释放栈帧的过程,调用时开辟栈帧空间,结束时释放
(言外之意不结束这层栈帧不释放)
递归每次调用都会开辟一个栈帧,如果递归的层数过多,不建议使用,容易内存溢出
每次开辟的栈帧空间,代码必须全部执行完毕之后才释放空间,在回到上一个栈帧执行没结束的代码
如果使用递归 , 需要给与一个跳出的条件,不能无限递归

尾递归:

在函数返回的时候,调用其本身,并且,return语句不包含表达式。

(简单来说是递归函数,且只调用自己的非表达式)

尾递归意义:

使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况.(需要Python解释器支持)

尾递归流程:

5.内存栈区堆区(了解内容)

单独讲栈堆是数据结构

栈:后进先出的一种数据结构

堆:排序后的一种树状数据结构

栈区堆区是内存空间

栈区:按照后进先出的数据结构(栈),无论创建或销毁都是自动为数据分配内存,释放内存

(系统自动做的)

堆区:按照排序后的树状数据结构(堆),可优先取出必要数据,无论创建或销毁都是手动分配内存,释放内存(程序员手动做的)

–内存中的栈区 : 自动分配 自动释放

–内存中的堆区 : 手动分配 手动释放

运行程序时在内存中执行,会因为数据类型的不同而在内存的不同区域运行

因不同语言对内存划分的机制不一,但大体来讲,有如下四大区域

–栈区: 分配局部变量空间.

–堆区: 是用于手动分配程序员申请的内存空间.

–静态区(全局栈区): 分配静态变量,全局变量空间.

–代码区(只读区,常量区): 分配常量和程序代码空间的.

栈区 堆区 静态区 代码区 都是内存中的一段空间

6.递归案例

1.使用递归实现任意数n的阶乘

普通实现


# 5! =5 *4*3*2*1
n = 5
total = 1
for i in range(n,0,-1):
    total *= i
print(total) # 120

递归实现

def jiecheng(n):
    if n <= 1:
        return 1
    return jiecheng(n-1) * n
    
print(jiecheng(2))
# jiecheng(1) => 1 
# jiecheng(2) => jiecheng(1) * 2 => 1 * 2
# jiecheng(3) => jiecheng(2) * 3 => 1 * 2 * 3
# jiecheng(4) => jiecheng(3) * 4 => 1 * 2 * 3 * 4
# jiecheng(5) => jiecheng(4) * 5 => 1 * 2 * 3 * 4 * 5
print(jiecheng(5))

代码解析:

去的过程:

n = 5 return jiecheng(n-1) * n => jiecheng(4) * 5

n = 4 return jiecheng(n-1) * n => jiecheng(3) * 4

n = 3 return jiecheng(n-1) * n => jiecheng(2) * 3

n = 2 return jiecheng(n-1) * n => jiecheng(1) * 2

n = 1 return 1

回的过程:

n = 2 return jiecheng(1) * 2 => 1 * 2

n = 3 return jiecheng(2) * 3 => 1 * 2 * 3

n = 4 return jiecheng(3) * 4 => 1 * 2 * 3 * 4

n = 5 return jiecheng(4) * 5 => 1 * 2 * 3 * 4 * 5

到此程序结束:

返回 1 * 2 * 3 * 4 * 5

print(“<====================>”)

2. 使用尾递归来实现任意数的阶乘

return 在哪调用,在哪返回

自己调用自己,且返回时非运算表达式,只是函数本身

特点:

尾递归只开辟一个空间,不会无限的开辟,在一个空间里面去计算最后的结果进行返回,比较节省空间,有的解释器支持尾递归的调用特点

但是cpython解释器目前不支持

写法:

所有运算的值都在函数的参数中计算完毕,最后返回运算的参数;

def jiecheng(n,endval):
    if n <= 1:
        return endval
    return jiecheng(n-1 , n * endval)
res = jiecheng(5,1) # 5*4*3*2*1
print(res)


代码解析:

去的过程

n = 5 ,endval = 1 return jiecheng(n-1 , n * endval) => jiecheng(4,51) => 51432

n = 4 ,endval = 51 return jiecheng(n-1 , n * endval) => jiecheng(3,514) => 51432

n = 3 ,endval = 514 return jiecheng(n-1 , n * endval) => jiecheng(2,5143) => 51432

n = 2 ,endval = 5143 return jiecheng(n-1 , n * endval) => jiecheng(1,51432) => 51432

n = 1 ,endval = 51432 if n <= 1 成立 return endval

endval = 51432

最下层空间的返回值 是 54321 最上层接收到的返回值也是 54321

最下层和最上层返回的结果是一致的,所以对于尾递归来说,只需要考虑去的过程,无需考虑回的过程即可完成;

(1) 优化代码1

def jiecheng(n,endval=1):

if n <= 1:

return endval

return jiecheng(n-1 , n * endval)

res = jiecheng(5,100) # 54321

print(res,“<00000>”)

(2)优化代码2 [把尾递归需要的参数值隐藏起来,避免篡改.]

def outer(n):

def jiecheng(n,endval=1):

if n <= 1:

return endval

return jiecheng(n-1 , n * endval)

return jiecheng(n,1)# 120 返回的是结果,不是闭包

print(outer(5))

(3)优化代码3(扩展)

闭包实现

def outer(n):
    endval = 1
    def jiecheng(n):
        nonlocal endval
        if n <= 1:
            return endval
        endval *= n 
        return jiecheng(n-1)
    return jiecheng
func = outer(5)
print(func(5),"<===111==>")

print("<================>")

3.使用递归来完成斐波那契数列

1 1 2 3 5 8 13 21 34 …

def feib(n):
    if n == 1 or n == 2:
        return 1
        
    # 上一个结果 + 上上个结果
    return feib(n-1) + feib(n-2)
print(feib(5))

代码解析:

n = 5 feib(5) => 3 + 2 => return 5

feib(4) + feib(3)

feib(3)+feib(2) feib(2)+feib(1) => 1 + 1 => 2

feib(2)+feib(1)+feib(2) => 1 + 1 + 1 => 3

可以直接计算出来斐波那契数列 第几个值是多少

相关文章
C4.
|
8天前
|
算法 搜索推荐 编译器
Python递归
Python递归
C4.
17 1
|
8天前
|
存储 算法 Python
python中递归调用过深(RecursionError)
【5月更文挑战第3天】
12 1
|
11月前
|
存储 算法 Python
Python递归算法详解
Python递归算法详解
|
6月前
|
Python
33 python - 递归函数
33 python - 递归函数
20 0
|
7月前
|
机器学习/深度学习 编译器 Python
Python-递归函数-L
Python-递归函数-L
|
11月前
|
Python
Python|递归应用
Python|递归应用
46 0
|
算法 Python
python的递归
把函数作为参数传入,这样的函数成为高阶函数,高阶函数是函数式编程的体现。函数式编程就是指这种高度抽象的编程范式
|
Python
Python 函数递归教程
Python 函数递归教程
56 0
|
Python
Python递归练习
Python递归练习自制脑图
57 0
Python递归练习
|
机器学习/深度学习 存储 人工智能
Python 递归函数
它能够把一个大型复杂的问题转化为一个与原问题相似的较小规模的问题来求解,用非常简洁的方法来解决重要问题。就像一个人站在装满镜子的房间中,看到的影像就是递归的结果。以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……程序设计可以让你的工作由几天节约至几个小时,好的算法可能可以让你的程序运行时间从几个小时节约至几秒钟。被计算了无数次,如果我们能在第一次计算出来后就存储下来,以供后面使用,会不会快些?,基例不需要再次递归,它是确定的表达式;
107 0
Python 递归函数