算法图解第三章--递归的深层次理解(学习笔记)

本文通过讲解递归的基线条件和递归条件,结合调用栈的概念,深入剖析递归工作原理。通过汉诺塔问题和阶乘函数的例子,阐述如何避免无限递归,以及递归调用栈的工作机制。同时指出,尽管递归使解决方案更直观,但可能导致性能损失和内存占用增加,循环在某些情况下可能是更好的选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、推荐:

如果想更好的理解本文章,你可以看往期文章:

算法图解第二章–选择排序法(数组,链表的进一步理解)(学习笔记)

二、基线条件和递归条件

def countdown():
    print i
    countdown(i-1)

这个代码会出现一直运行的情况(你可以按Ctrl+C停止运行),之所以会出现这样的情况是因为没有告诉它何时停止递归,所以每个递归函数都应该包含两部分:基线条件和递归条件,去避免形成无限循环。改良后的代码:

def countdown():
    print i
    if i<=1:
        return
    else:
        countdown(i-1)

三、栈

大部分人学函数时就了解了递归,诸如的经典例子便是汉诺塔问题,如果想要了解的点击下面:
用递归来解决汉诺塔问题(超详细的个人解读)(Python)

我们先来理解一个重要的编程概念——调用栈(call stack)。

调用栈不仅对编程来说很重要,使用递归时也必须理解这个概念。

那什么是栈呢?
在这里插入图片描述
(如果想要全本电子书可以去我的主页里简介获取)
这是书上的解释,可能你没有耐心去理解,我总结了一下,看图理解:

在这里插入图片描述

你可以假设这是一个只有一个出口,三面封闭的盒子。

你如果往里面放东西,先放进去的东西就会被后放进去的东西压在下面,而当你想要去拿出被压在下面东西的时候,你就必须先取出上面的东西(也就是你后放进去的东西).

其实书上的例子就好比你写了一堆标签,然后一个钉子把他们钉在了墙上,你就只能从外面一个一个拿(注意后面写的标签在外面,也就是先依次写一堆标签,然后反向依次处理)。

四、调用栈

既然你都理解了“栈”,那么如何调用它呢?我们先写一段简单代码辅助理解。

def greet2(name):
    print("你好!{}".format(name))
def bye(name):
    print("再见!{}".format(name))
def greet(name):
    print("Hello!{}".format(name))
    greet2(name)
    print("最近好吗?")
    bye(name)
greet("maggie")

结果:

Hello!maggie
你好!maggie
最近好吗?
再见!maggie

下面来详细介绍函数运行情况(我觉得这本书这里写得很明白,就照着书说一下):
首先,你调用greet(“maggie”),计算机将首先为该函数调用分配一块内存。如图:
在这里插入图片描述

我们来使用这些内存。

变量name被设置为“maggie”,这需要存储到内存中。
在这里插入图片描述

每当你调用函数时,计算机都像这样将函数调用涉及的所有变量的值存储到内存中。

接下来,你打印 Hello!maggie,再调用greet2(“maggie”)。

同样,计算机也为这个函数调用分配一块内存。
在这里插入图片描述

计算机使用一个栈来表示这些内存块,其中第二个内存块位于第一个内存块上面。

你打印 你好!maggie,然后从函数调用返回。

此时,栈顶的内存块被弹出。

在这里插入图片描述

现在,栈顶的内存块是函数greet的,这意味着你返回到了函数greet。

当你调用函数greet2时,函数greet只执行了一部分。

这是本节的一个重要概念:调用另一个函数时,当前函数暂停并处于未完成状态。该函数的所有变量的值都还在内存中。执行完函数greet2后,你回到函数greet,并从离开的地方开始接着往下执行,

首先打印 最近好吗?,再调用函数bye。

在这里插入图片描述

在栈顶添加了函数bye的内存块。然后,你打印再见!maggie,并从这个函数返回。在这里插入图片描述

现在你又回到了函数greet。由于没有别的事情要做,你就从函数greet返回。这个栈用于存储多个函数的变量,被称为调用栈

五、递归调用栈

先写一段阶乘代码辅助理解下:

def fact(x):
    if x==1:
        return 1
    else:
        return x*fact(x-1)

简单理解的话就是:
假如x=3,,先运行else语句,就会得到 xfact(x-1),此时x为3,也就是3fact(2),

fact(2)函数会运行else语句则返回xfact(x-1),此时x为2,也就是2fact(1),综合上一句就是32fact(1)

fact(1)函数会运行if 语句,则返回1,综上所述便是311=6,

其实递归的话就是只有运行到fact(1)语句才会运行基线条件,也只有运行了基线条件,递归才会结束

然后反向依次返回(就是依次返回1,2,6)。

注意每个fact调用都有自己的x变量。在一个函数调用中不能访问另一个的x变量

.这是我的理解,可能有一些不足,下面放上书上的理论理解:

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

再次注意
每个fact调用都有自己的x变量。在一个函数调用中不能访问另一个的x变量。
其实学下来,你会发现,使用栈虽然很方便,但是也要付出代价

存储详尽的信息可能占用大量的内存。每个函数调用都要占用一定的内存

如果栈很高,就意味着计算机存储了大量函数调用的信息

所以递归只是让解决方案更清晰,并没有性能上的优势。

实际上,在有些情况下,使用循环的性能更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃辣的IT男

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值