Python 递归 深入理解递归 Python递归剖析,绝对让你看懂!

递归剖析

  • 递归真的很重要,之前学的时候,学的一知半解,以为真正了解,每次想到递归,就记得一句:返回给函数的调用者,嗯?函数调用者,你是说外部,还是内部啊?疑问太多了,还有就是被告知一句:递归能解决的问题,循环都能解决,所以就更加不重视递归了!直到接触算法后,在解决问题时,最快,最容易理解的解法就是递归,但是此时的递归却是看不太懂为什么要这样做!我先来说下,在算法中遇到可以用递归轻松完成的:希尔排序、归并排序、快速排序、反转链表及各种反转问题、二叉树的深度遍历、二叉树的各种题基本可以用、全排列…还有算法步骤里需要用到,太多了,所以,递归非常重要!“To iterate is human, to recurse divine 迭代是人,递归是神”,接下来,我也是看了很多算法书和视频,重点来整理下递归!
递归的两个过程
  • 首先“递归”包括两个过程:递“去”的过程,“归”回的过程!先从一个简单的递归函数讲起
def di_gui(n):
    print(n, "<===1====>")
    if n > 0:
        di_gui(n - 1)
    print(n, '<===2====>')


di_gui(5) # 外部调用后打印的结果是?
  • 递归的执行过程:首先,递归会执行“去”的过程,只需要满足终止条件,就会一直在函数内,带着更新的参数,调用函数自身,注意:到内部调用函数, 以下面的代码不会被执行,而是暂停阻塞;此时 随着函数每调用一次自身,还没有触发 返回值和到达终止条件,等同于在原来的基础上不断“向下/向内”开辟新的内存空间,记住,每次调用一次函数,就不是处在同一空间(想象下《盗梦空间》里的情景,梦中梦,都是处于不同的空间)
    在这里插入图片描述

  • 什么时候“递”去的过程结束?记住有两种情况>>> 一是:当前这层空间函数全部执行结束(终止条件),二是:执行到了return 返回值,直接返回

  • 重点来理解下,首先是一,看上面的列子,例子中没有return,但是不断的调用后,最终还是停止了,因为最后n=0时,di_gi(0)还是去调用,从上往下执行时,遇到if n>0 它被终止了,走不下去了,表明,自己能到达的这层空间已经全部执行完毕;接下来请原地返回吧,返回到哪里?返回到函数的调用者,好我们返回到 di_gui(0),把“到内部调用函数” 以下的代码全部执行完;执行完,看代码走到末尾,以为走出了最外层函数?注意了,此时它所处的空间并不是最外层哦,因为之前它被调用就在空间里面,所以回到的是 di_gui(1)的这一层空间,现在才是真正的开始“回”,所以继续把di_gui(1)的这一层空间,“到内部调用函数”以下的代码全部执行完,回到di_gui(2)的这一层空间…直到到达最开始 从外部调用,让参数5进入的最外层空间位置,才走出来!表示《盗梦空间》里,柯布醒了!
    在这里插入图片描述
    回来看下,代码输出的结果:5 4 3 2 1 00 1 2 3 4 5 ( 注意00这两个是在同一层空间哦)
    在这里插入图片描述

  • 从内存角度(本质)来分析:每调用一次函数,都会单独开辟一份栈帧空间,递归函数就是不停的开辟和释放栈帧空间的过程,具体来理解下:一个普通函数从执行到结束,就是一个开辟空间和释放空间的过程;而递归函数是在调用最外层函数时,先开辟一个最外层空间,每调用一次自身,就会在最外层空间内,再自己开辟本次的空间(所以递归耗内存)(还有一种说法是,不断的本次空间的基础上再开辟空间,等于是不断的嵌套,其实这两种说法本质上是一样的,因为信息都可以做到不共享),空间之间如果不通过参数传递或者用return 返回值,信息是不共享的,如下图↓↓↓

在这里插入图片描述
在这里插入图片描述

  • 递归的数据共享情况:递归每一层间的数据是独立的,不共享,但是可以通过参数或者返回值来形成共享,参数具体在传入的是“引用类型”(比如列表)

  • 递归 必须要有一个出口,如果没有出口就会重复调用,不断的开辟栈帧空间

# 获取最大递归层数:
import sys
res=sys.getrecursionlimit()
print(res) # 结果:1000
# sys.setrecursionlimit(800) 可以自己设置最大递归层数
  • 解释含有 return 返回值的递归,记住 return的返回流程是:先计算,再返回 ,来看下一段求阶乘的代码:
def jie_cheng(n):
    if n <= 1:
        return 1
    return n * jie_cheng(n - 1)
    
print(jie_cheng(5))

在这里插入图片描述

  • return 是返回到函数调用者,递归函数和普通函数也是一样的,所以递归最后一层空间走到尽头(一是:指向完毕,二是:遇到return,回顾一下而已)遇到return,就要开始返回了,返回到它的调用者(即是阻塞的位置),直到回到最外层空间
  • 如果上面的内容看懂了的话,试着解析下:下面这段代码的执行流程
def get_num(num):
    if num > 2:
        get_num(num - 1)
    print(num)


get_num(4) # 输出结果为:2 3 4

在这里插入图片描述

代码变化一下:

def get_num(num):
    if num > 2:
        get_num(num - 1)
    else:
        print(num)


get_num(4) # 输出结果为 2

'''
解析一下:加了else后,首先代码区有两个分支,
在num>2时,执行会有递归,当n=4 是开辟一层空间;
n=3时开辟一层空间,此时 get_num(2) 再开辟一个空间,
当它从上往下执行过程中,在他本层空间遇到if num>2 不成立,所以走分支 else,直接打印出来;
此时代码还没结束,回到上一层空间,num=3, num>2 已经进入了if 不会走else,
num=4 也不会走else,所以这两层空间没有值输出!
'''
return 返回值 详解
  • 上面这一大部分,就算是递归入门了,接下来才刚刚开始哦!还要继续讲 return ;先来看下这几段代码:求全排列的一部分递归代码,试着分别写出运行结果,并依次分析原因↓↓↓
# 例1:
def fullpermutation(list):
    if list is None:
        return None
    if len(list) == 1:
        return [list]
    res = []
    pivot = list[0]
    remain = fullpermutation(list[1:])
    print(remain)


print(fullpermutation([1, 2, 3]))  
'''
输出结果为:
[[3]]
None
None
'''
  • 递归只会在两种情况下触发“回”的过程,上述是在最后一层空间是碰到了return,所以给回到它的调用处(阻塞处),因为return会给调用者返回一个值,所以在本层空间,remain接收到了一个值:[[3]];接着执行下面的代码,即是打印remain,所以输出“[[3]]”,执行完print,等于回到了上一层空间,又到了调用处(阻塞处),那么这层空间还有返回值吗?答案是没有,所以“最后一层空间是碰到了return 给它返回的值 只会给最后一层使用”,所以接下来两层都是打印空!
# 例2:
def fullpermutation(list):
    if list is None:
        return None
    if len(list) == 1:
        return [list]
    res = []
    pivot = list[0]
    return fullpermutation(list[1:])


print(fullpermutation([1, 2, 3]))
'''
输出结果为:
[[3]]
'''
  • 这次是,在最后一层返回时,获得了一个返回值 [[3]] ,然后回到上一层时,前面又有return 表示需要把本层的返回值,返回到上层的接收处,重复,直到回到最外层,这个从底层传上来的返回值,一直传到了最外层,所以才打印出来的,只有最后一层,但是每次一层都获得了返回值,和例子1 后面两层没有返回值是不同的哦!
# 例3:
def fullpermutation(list):
    if list is None:
        return None
    if len(list) == 1:
        return [list]
    res = []
    pivot = list[0]
    remain = fullpermutation(list[1:])
    print(list
评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值