函数之递归[通俗易懂]

递归前戏在讲今天的内容之前,我们先来讲一个故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座

大家好,又见面了,我是你们的朋友全栈君。

递归

前戏

在讲今天的内容之前,我们先来讲一个故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢……这个故事你们不喊停我能讲一天!我们说,生活中的例子也能被写成程序,刚刚这个故事,让你们写,你们怎么写呀?

while True:
    story = "
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?   
    "
    print(story)

你肯定是要这么写的,但是,现在我们已经学了函数了,什么东西都要放到函数里去调用、执行。于是你肯定会说,我就这么写:

def story():
    s = """
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?
    """
    print(s)
    
while True:
    story()

但是大家来看看,我是怎么写的!

def story():
    s = """
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?
    """
    print(s)
    story()
    
story()

先不管函数最后的报错,除了报错之外,我们能看的出来,这一段代码和上面的代码执行效果是一样的。

递归的概念:

在一个函数内部调用这个函数自身我们就可以将其称为递归函数

递归其实是倆个不同的过程:

递:是整个函数执行的过程是从上到下的顺序

归:是将执行的结果返回来是从下到上的顺序

def  story():
    info = '''
    从前有个山,山里有座庙,庙里老和尚讲故事,
    讲的什么呢?   
    '''
    print(info)
    story()

story()    

上面一个简单的示例诠释了递归函数概念:在story这个函数中调用了story函数自身那么我们就可以称story这个函数为递归函数。

我们现在知道了什么是递归函数,但是在执行这个函数的时候会发现执行以后会报错:RecursionError: maximum recursion depth exceeded while calling a Python object

这是为什么呢?是不是我们的递归函数写错了呢?不然为什么会报错呢?这就涉及到了一个新的知识点—递归函数的最大深度

递归的最大深度深度

什么是递归函数的最大深度呢?

  我们执行递归函数如果不受到外力的阻止会一直执行下去。但是我们之前已经说过关于函数调用的问题,每一次函数调用都会产生一个属于它自己的名称空间,如果一直调用下去,就会造成名称空间占用太多内存的问题,于是python为了杜绝此类现象,强制的将递归层数控制在了997

  怎么证明递归的最大深度是997呢?

函数之递归[通俗易懂]
函数之递归[通俗易懂]

def foo(n):
    print(n)
    n += 1
    foo(n)
foo(1)

测试递归最大深度

通过执行上述代码知道在程序没有报错之前执行的 最大值就是997,当然997是python为了我们程序的内存优化所设定的一个默认值,我们当然还可以通过一些手段去修改它:

函数之递归[通俗易懂]
函数之递归[通俗易懂]

import sys
print(sys.setrecursionlimit(100000))

修改递归最大深度值

我们可以通过这种方式来修改递归的最大深度,刚刚我们将python允许的递归深度设置为了10w,至于实际可以达到的深度就取决于计算机的性能了。不过我们还是不推荐修改这个默认的递归深度,因为如果用997层递归都没有解决的问题要么是不适合使用递归来解决要么是你代码写的太烂了~~~

看到这里,你可能会觉得递归也并不是多么好的东西,不如while True好用呢!然而,江湖上流传这这样一句话叫做:人理解循环,神理解递归。所以你可别小看了递归函数,很多人被拦在大神的门槛外这么多年,就是因为没能领悟递归的真谛。而且之后我们学习的很多算法都会和递归有关系。来吧,只有学会了才有资本嫌弃!

递归的深入了解

通过对初始递归这个小结的了解  我们对递归也有了一定的了解 当然了只是初步的了解 ,那么就让我们再来深入的了解一下吧 毕竟这样才可以掌握的更扎实嘛

函数之递归[通俗易懂]
函数之递归[通俗易懂]

现在你们问我,alex老师多大了?我说我不告诉你,但alex比 egon 大两岁。
你想知道alex多大,你是不是还得去问egon?egon说,我也不告诉你,但我比武sir大两岁。
你又问武sir,武sir也不告诉你,他说他比金鑫大两岁。
那你问金鑫,金鑫告诉你,他40了。。。
这个时候你是不是就知道了?alex多大?
1  金鑫   40
2  武sir    42
3  egon  44
4  alex  46
你为什么能知道的?
首先,你是不是问alex的年龄,结果又找到egon、武sir、金鑫,你挨个儿问过去,一直到拿到一个确切的答案,然后顺着这条线再找回来,才得到最终alex的年龄。这个过程已经非常接近递归的思想。我们就来具体的我分析一下,这几个人之间的规律。
age(4) = age(3) + 2 
age(3) = age(2) + 2
age(2) = age(1) + 2
age(1) = 40
那这样的情况下,我们的函数应该怎么写呢?

def age(n):
    if n == 1:
        return 40
    else:
        return age(n-1)+2

print('alex的年龄是:%s'%age(4))

猜年龄深入了解递归

怎么样通过上面的例子相信你一定对递归有了一个更深刻的理解了吧  那么就请牢牢的记住吧  它的用处非常的大哦,不相信你会后悔的!!!

递归和三级菜单

函数之递归[通俗易懂]
函数之递归[通俗易懂]

数据结构:

menu = {
    '北京':{
        '海淀':{
            '五道口':{
                'soho':{},
                '网易':{},
                'google':{}
            },
            '中关村':{
                '爱奇艺':{},
                '汽车之家':{},
                'youku':{},
            },
            '上地':{
                '百度':{},
            },
        },
        '昌平':{
            '沙河':{
                '老男孩':{},
                '北航':{},
            },
            '天通苑':{},
            '回龙观':{},
        },
        '朝阳':{},
        '东城':{},
    },
    '上海':{
        '闵行':{
            "人民广场":{
                '炸鸡店':{}
            }
        },
        '闸北':{
            '火车战':{
                '携程':{}
            }
        },
        '浦东':{},
    },
    '山东':{},
}


需求:
可依次选择进入各子菜单
可从任意一层往回退到上一层
可从任意一层退出程序

#递归方式实现
def show_Menu(ch):
    for s in ch:
        print(s)
    print('返回/退出')
    p = input('您选择是')
    if p == '退出':
        exit()
    elif p == '返回' and ch != menu:
        return
    else:
        if p in ch:
            show_Menu(ch[p])
        show_Menu(ch)
show_Menu(menu)

递归实现三级菜单

递归和二分算法

递归我们已经知道是怎么回事,那么算法又是怎么回事呢?很简单啊,你通过字面就可以解释啊:就是计算的方法嘛。那么下面我们就通过示例来看一下什么是二分算法吧

如果有一个列表,让你从这个列表中找到66的位置,你要怎么做?

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

l.index(66) 这不就解决问题了吗

这确实可以解决问题,是因为我们用的index方法内部提供了__next__()方法就相当于for循环遍历了整个列表,我们的示例数据量比较小这样做可以但是如果有一个非常大的数据达了几万或者几十万的话这样做会占用很大的内存导致程序运行缓慢甚至崩溃 这该怎么办呢 这就用到了我们要学习的二分法

二分法概念

其实就是一种通过不断的排除不可能的东西,来最终找到需要的东西的一种方法.所以可以理解成排除法.之所以叫二分,是因为每次排除都把所有的情况分成"可能""不可能"两种,
然后抛弃所有"不可能"的情况.最正统的二分法中,是每次排除都可以排除掉一半的情况,这样子的寻找效率是很高的.
 ps:二分查找算法 必须处理有序的列表
函数之递归[通俗易懂]
函数之递归[通俗易懂]

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

def find(l,aim):
    mid_index = len(l) // 2
    if l[mid_index] < aim:
        new_l = l[mid_index+1 :]
        find(new_l,aim)
    elif l[mid_index] > aim:
        new_l = l[:mid_index]
        find(new_l, aim)
    else:
        print('找到了',mid_index,l[mid_index])

find(l,66)

二分法实现数据查找

ps:使用二分法查找数据数据必须是有序的列表方可

函数之递归[通俗易懂]
函数之递归[通俗易懂]

l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

def find(l,aim,start = 0,end = None):
    end = len(l) if end is None else end
    mid_index = (end - start)//2 + start
    if start <= end:
        if l[mid_index] < aim:
            return find(l,aim,start =mid_index+1,end=end)
        elif l[mid_index] > aim:
            return find(l, aim, start=start, end=mid_index-1)
        else:
            return mid_index
    else:
        return '找不到这个值'

进阶(终极版本)

 

 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/124536.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 游戏建模学习经验分享

    游戏建模学习经验分享最近通过很多师弟的交流,我发现游戏建模初学者大多存在三个大问题,一是工具的使用不够熟练,甚至有些功能还不知道,二是对布线的规范没有太大的要求和了解,三是对游戏制作流程不清晰和板绘下的功力不够,对贴图制作用工少,甚至有些人还处于一直做白膜的阶段,那么对大多说想要要学游戏建模的学习者想要学什么:低模,高模制作,贴图材质,动作特效。毕竟很多人学的并没有那么快,建模实质就是孰能生巧,做的东西多了,遇到问题多了,解决之后就会学的更多。今天就跟大家聊一聊目前我遇到新手关于建模方面的问题。1:工具使用不熟练很多师

  • G1收集器图解

    G1收集器图解G1在堆上分配内存和其他的GC有点不一样。现在我们来一步一步看下G1系统。1、G1堆结构G1的堆结构就是把一整块内存区域切分成多个固定大小的块。在JVM在启动时来决定每个小块,也就是region的大小。JVM一般是把一整块堆切分成2000个小region。然后每个小region从1到32Mb不等。2、G1内存分配事实上,这些region最后又被…

  • Android-json解析(三):原生JSONObject+JSONArray的解析、遍历及生成等

    Android-json解析(三):原生JSONObject+JSONArray的解析、遍历及生成等一、JSONObject和JSONArray的数据表示形式JSONObject的数据是用{}来表示的,例如:{&amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;id&amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;:&amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;123&amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,

  • linux中kill命令详解_linux kill函数

    linux中kill命令详解_linux kill函数linuxkill命令详解一、命令格式:kill[参数][进程号]二、命令功能:发送指定的信号到相应进程。不指定型号将发送SIGTERM(15)终止指定进程。如果任无法终止该程序可用“-KILL”参数,其发送的信号为SIGKILL(9),将强制结束进程,使用ps命令或者jobs命令可以查看进程号。root用户将影响用户的进程,非root用户只能影响自己的进程。三、命令参数:-l信号,若果不加信号的编号参数,则使用“-l”参数会列出全部的信号名称-a当处理当前进程时,不

    2022年10月29日
  • phpstudy搭建网站并实现外网访问[通俗易懂]

    phpstudy搭建网站并实现外网访问[通俗易懂]最近服务器被黑客攻击,挂了,只能重装系统,还好网站都在本地有备份.于是又苦逼的搭建服务器吧,这里我没有使用iis的服务器而是用了Apache服务器,并用的phpstudy集成.搭建玩ftp,网站上传完,在本地设置完域名信息,但是在外网始终无法访问,ps:域名之前就已经设置完解析的.然后一通百度,都是简单的介绍并没有解决问题.于是考虑到可能是防火墙的原因.结果发现防火墙,…

  • 程序猿的量化交易之路(26)–Cointrader之Listing挂牌实体(13)

    程序猿的量化交易之路(26)–Cointrader之Listing挂牌实体(13)

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号