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

一、推荐:

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

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

二、基线条件和递归条件

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变量。
其实学下来,你会发现,使用栈虽然很方便,但是也要付出代价

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

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

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章