python基础----递归函数

递归函数

先给大家举个简单的例子,来认识一下递归函数

小时候经常讲的一个故事,从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢.....这个故事我们用代码实现一下

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

 

用函数的调用来实现

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

 

 还可以用别的方式来实现

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

 

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

最后这种方式就是用递归的方式来实现的,让我们正式认识一下递归函数

递归函数定义及相关

递归函数的定义——在一个函数里再调用这个函数本身

就像上面的story()函数,在story()函数里面再调用story()这个函数本事,这种函数的调用的方式就是递归

 

递归的最大深度——997

正如刚刚调用story()看到的,递归函数如果不受到外力的阻止会一直执行下去。但是我们之前已经说过关于函数调用的问题,每一次函数调用都会产生一个属于它自己的名称空间,如果一直调用下去,就会造成名称空间占用太多内存的问题,于是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(age(4))

 

 

例二

阶乘用递归函数实现

5的阶乘   5! = 5*4*3*2*1  = 5*4!

4的阶乘   4!=4*3*2*1     = 4*3!

3的阶乘   3!=3*2*1        = 3*2!

2的阶乘   2!=2*1            =2*1!

1的阶乘   1!=1               =1

这样可以总结规律为  n! =n * (n-1)!

def factorial(n):
    if n == 1:
        return 1
    return n*factorial(n-1)

print(factorial(5))
print(factorial(4))

 

 

例三

斐波那契数, 问第n个斐波那契数是多少

归纳规律:

F(n) = F(n-1)+F(n-2)
F(6) = F(5)+F(4)
F(5) = F(4)+F(3)
F(4) = F(3)+F(2)
F(3) = F(2)+F(1)
F(2) = 1
F(1) = 1

 def fib(n):
     if n == 1 or n==2:
         return 1
     return fib(n-1) + fib(n-2)

 print(fib(10))
 print(fib(50)) #执行时间特别长

 

当我们执行算第50个斐波那契数的时候,代码运行的时间特别长,这是为什么呢?因为这个函数里包含了两次递归调用,如下图,函数只有一个递归调用如下右边的调用过程,而有两个递归调用就如左边的调用过程,这样就一目了然。

所以用递归函数,最好不用在函数里有两个递归调用。

 

用递归函数实现 三级菜单

menu = {
    '北京': {
        '海淀': {
            '五道口': {
                'soho': {},
                '网易': {},
                'google': {}
            },
            '中关村': {
                '爱奇艺': {},
                '汽车之家': {},
                'youku': {},
            },
            '上地': {
                '百度': {},
            },
        },
        '昌平': {
            '沙河': {
                '老男孩': {},
                '北航': {},
            },
            '天通苑': {},
            '回龙观': {},
        },
        '朝阳': {},
        '东城': {},
    },
    '上海': {
        '闵行': {
            "人民广场": {
                '炸鸡店': {}
            }
        },
        '闸北': {
            '火车战': {
                '携程': {}
            }
        },
        '浦东': {},
    },
    '山东': {},
menu
def Tmenu(menu):
    # menu = menu if type(menu) is list else [menu]
    while True:
        for i in menu: print(i)
        item = input(">>>请输入:")
        if item == 'break':
            break
        if item == 'upper':
            return item
        if item in menu.keys():
             Tmenu(menu[item])
        else:
            print("输入有误,请重新输入!!!")

Tmenu(menu)
递归函数实现三级函数(自己写的)

 

优化版

 def threeLM(dic):
     while True:
         for k in dic:print(k)
         key = input('input>>').strip()
         if key == 'b' or key == 'q':return key
         elif key in dic.keys() and dic[key]:
             ret = threeLM(dic[key])
             if ret == 'q': return 'q'
 
 threeLM(menu)
递归函数实现递归函数(优化版)

 

 

 递归函数与二分查找算法

 二分查找算法

 

总结

 

# 如果递归次数太多,就不适合使用递归来解决问题
# 递归的缺点 : 占内存
# 递归的优点: 会让代码变简单

注意:

# 超过最大递归限制的报错
# 只要写递归函数,必须要有结束条件。

  

posted @ 2018-11-16 15:37  卖菜大叔  阅读(23)  评论(0)    收藏  举报