递归算法

函数直接或者间接调用自身就是递归。递归需要有边界条件,递归前进段、递归返回段。递归一定要有边界条件。当边界条件不满足的时候,递归前进。当边界条件满足的时候,退出递归。

举例,斐波那契数列:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……

先前的一般做法是利用循环:

a = 0
b = 1
n = 10
for i in range(n - 1):
    a, b = b, a + b
else:
    print(b)

如果设 F(n) 为第 n 项 (n ∈ N*),那么斐波那契数列可以写成:F(n) = F(n-1) + F(n-2)。如果再设置F(0) = 0,,F(1) = 1,那么通过F(n) = F(n-1) + F(n-2) 就可以算出任意项,这就是递归方法:

def fib(n):
    return 1 if n < 3 else fib(n-1) + fib(n-2)

一、递归的要求

  1. 递归一定要有退出条件,递归调用一定要执行到这个条件退出。没有退出条件的递归调用,就是无限调用

  2. 调用的深度不宜过深

    • python 对递归调用做了限制,以保护解释器
    • 超过递归深度,抛出 RecursionError: maximum recursion depth exceeded
    • sys.getrecursionlimit() 可以查看限制的次数,默认是1000,不建议修改

二、递归的性能

将上面的斐波那契数列的两种方法进行对比,得到以下结果:

  • 循环的代码复杂,但是只要不是死循环,可以进行多次迭代直至算出结果
  • 递归的代码简单,但是只能获取到最外层的函数调用,内部函数递归结果都是中间结果。而且给定一个n都要进行 近2n次 的递归调用,深度越深,效率越低,为了获取整个斐波那契数列数列而不是单项数字,还得要在外面再套一层循环才行,效率更低了。
  • 递归还有深度限制,如果递归复杂,函数反复压栈,栈内存很快溢出

三、提高递归的性能

改进后的递归代码:

def fib(n, a=0, b=1):
    a, b = b, a+b
    if n == 1:
        return a
    else:
        fib(n-1)

解析:

  • 与循环的思路类似
  • 参数n是边界条件,用n计数
  • 上一次的计算结果直接作为函数的实参
  • 效率很高,和循环相比,性能相近,这是因为递归的次数接近减半了。所以说不是说递归的性能不好,只是递归有深度限制

四、间接递归

def foo1():
    foo2()
    
def foo2():
    foo1()
    
foo1()

间接递归,就是通过其他函数来调用自身。但是,构成循环递归调用是很危险的,但是往往在代码复杂的时候会出现这种情况,要避免这种情况的发生。

五、总结

  • 递归是一种很自然的表达,符合逻辑思维
  • 递归的效率相对运行效率低,因为没调用一次函数都要开辟栈帧
  • 递归有深度限制,如果递归层次太深,函数反复压栈,栈内存很快就溢出了
  • 如果是有限次的递归,可以使用递归,或者使用循环代替,循环的代码稍微复杂,但是只要不是死循环,可以迭代多次直至算出结果
  • 绝大多数递归都可以使用循环解决
  • 即是代码可以简化,但是<font color=red>能不用递归就不要用</font>

六、递归练习题

关于递归的解法,一般有两种:

一种如同数学公式

一种类似循环,相当于循环的的改版,将循环迭代,变成函数调用的压栈

习题1:求n的阶乘

# 阶乘公式
def factorial(n):
    if n < 2:
        return 1
    else:
        return factorial(n-1) * n
# 循环完成
n = 5
fac = 1

for i in range(n, 0, -1):
    fac = fac * i
    print(fac)
# 循环变递归
def factorial(n, fac=1):
    fac = fac * n # 循环体
    if n < 2: # 边界
        return fac
    else:
        return factorial(n-1, fac) # 进行下一次函数调用,通过参数将每一次的循环体往下传递

习题2:解决猴子吃桃问题

猴子第一天摘下若干个桃子,当即吃掉一半,还不过瘾,有多吃了一个。第二天早上又将剩下的桃子吃去一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想吃时,只剩下一个桃子了。问一开始摘了多少个桃子。

# 递归
def f(n=1):
    if n == 10:
        return 1
    else:
        return (f(n-1) + 1) * 2
        
print(fn)
# 循环
peach = 1

for i in range(1, 10):
    peach = (peach + 1) * 2
    
print(peach)
# 循环改递归
def peach(days=10, p=1):
    if days == 1:
        return p
    p = (p + 1) * 2
    return peach(days-1, p)

print(peach())

习题三:将一个数逆放入列表中

例如1234 ==> [4, 3, 2, 1],一个数字1234被切片后,变成了4项,逆序放在了列表中

# 字符串切片

data = str(1234)

def reverse(data):
    if not data:
        return []
    return [data[-1]] + reverse(data[:-1])

print(reverse(data))

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def reverse01(data, newdata=[]):
    newdata = newdata + [data[-1]]
    if not data:
        return newdata
    return reverse(data[:-1])

print(reverse(data))
# 使用整数整除取模
data = 1234 

def revert(data, target=None):
    if target is None: # 一开始就创建空列表作为容器
        target = []

    x, y = divmod(data, 10) # divmod(x, y) ==> Return the tuple (x//y, x%y)
    target.append(y) # 存储个位数

    if x == 0: # 边界
        return target
    return revert(x, target) # 每一次迭代都会取出data里的一位,将少了一位的data和容器继续往下传

print(revert(data))
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • # 穷举(枚举、暴力、强力)算法 ## 基本思想 在可能的解空间中穷举出每一种可能的解,并对每一个可能解进行判断,...
    Tenloy阅读 5,121评论 0 7
  • 计算机科学的新学生通常难以理解递归程序设计的概念。递归思想之所以困难,原因在于它非常像是循环推理(circular...
    启明_b56f阅读 7,366评论 0 20
  • 一、递归定义 如果函数中包含了对其自身的调用,该函数就是递归的; 递归(Recursion),在数学与计算机科学中...
    惑也阅读 10,850评论 0 4
  • To iterate is human, to recurse, divine.人理解迭代,神理解递归。 什么是递...
    周闖阅读 552评论 0 0
  • 2017年过了1/4了,目标好难坚持啊。。突然觉得,机会就好像调显微镜,但显微镜可以来回重调,机会却转瞬即逝……(...
    亿雪笙阅读 367评论 0 0