Worth Waiting For

函数之递归

递归

前戏

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

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 '找不到这个值'
进阶(终极版本)

 

 

posted on 2017-08-09 15:56  WorthWaitingFor  阅读(294)  评论(0编辑  收藏  举报

导航