Python入门篇-递归函数(recursion

                                      作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.递归概述

  (1)函数直接或者间接调用自身就是递归;

  (2)递归需要有边界,递归前进段,递归返回段;

  (3)递归一定要有边界条件;

  (4)当边界条件不满足的时候,递归前进;
  
  (5)当边界条件满足的时候,递归返回;

二.递归案例

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com import sys print("默认的递归调用次数限制0是:{} 次".format(sys.getrecursionlimit())) sys.setrecursionlimit(20000) #我们修改默认的递归调用次数 print("当前递归调用次数限制是:{} 次".format(sys.getrecursionlimit()))
"""
递归要求:
递归一定要有退出条件,递归调用一定要执行到这个退出条件。没有退出条件的递归调用,就是无限调用。
递归调用的深度不宜过深:
Python对递归调用的深度做了限制,以保护解释器
超过递归深度限制,抛出:"RecursionError: maxinum recursion depth exceeded" 即超过了最大递归深度
sys.getrecursionlimit()
""" """
斐波拉契数字推理公式:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)
"""
def fib(n):
return 1 if n < 2 else fib(n-1) + fib(n-2) for i in range(20):
print(fib(i), end=' ') #以上代码执行结果如下:
默认的递归调用次数限制0是:1000 次
当前递归调用次数限制是:20000 次
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765

三.递归的性能

1>.使用for循环打印斐波拉契前35个数字

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com import datetime start = datetime.datetime.now() pre = 0 cur = 1 # No1 print(pre, cur, end=' ') n = 35 for i in range(n-1):
pre, cur = cur, pre + cur
print(cur, end=' ') delta = (datetime.datetime.now() - start).total_seconds() print("\n递归调用打印斐波拉契钱35个数字的时间为:{}".format(delta)) #以上代码直接结果如下:
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465
递归调用打印斐波拉契钱35个数字的时间为:0.0

2>.使用递归方式打印斐波拉契前35个数字

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com import datetime n = 35 start = datetime.datetime.now() def fib(n):
return 1 if n < 2 else fib(n-1) + fib(n-2) for i in range(n):
print(fib(i), end=' ') delta = (datetime.datetime.now() -start).total_seconds() print("\n递归调用打印斐波拉契钱35个数字的时间为:{}".format(delta)) #以上代码直接结果如下:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465
递归调用打印斐波拉契钱35个数字的时间为:5.93134

3>.递归优化方案 

  循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果

  fib函数代码极简易懂,但是只能获取到最外层的函数调用,内部递归都是中间结果。而且给定一个n都要进行2n次递归,深度月神,效率月底。为了获取斐波那契数列需要外面在套一个n次的循环,效率就更低了。

  递归还有深度限制,如果递归复杂,函数反复压栈,栈内存很快就溢出了

  思考:这个极简的递归代码能否提高性能呢?下面是优化后的代码,效率有明显的提示,代码如下所示:
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com import datetime start = datetime.datetime.now() pre = 0 cur = 1 # No1 print(pre, cur, end=' ') """
改进后的fib函数和循环的思想类似
参数n是边界条件,用n来计算
上一次的结果直接作为参数的实参
效率很高
和循环比较,性能相近。所以并不是说递归一定效率低下。但是递归有深度限制。
"""
def fib(n, pre=0,cur=1): # recursion
pre, cur = cur, pre + cur
print(cur, end=' ')
if n == 2:
return
fib(n-1, pre, cur) fib(35) delta = (datetime.datetime.now() -start).total_seconds() print("\n递归调用打印斐波拉契钱35个数字的时间为:{}".format(delta)) #以上代码直接结果如下:
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465
递归调用打印斐波拉契钱35个数字的时间为:0.0

4>.间接递归

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def foo():
bar() def bar():
foo() """
我们这里看起来只是调用foo函数,但是进入foo函数中的函数体中我们发现调用bar函数,进入bar函数中的函数体中我们发现它
又再次调用foo函数。因此我们执行该代码后会发现跑出来异常:“RecursionError: maximum recursion depth exceeded”.
这就形成了间接递归,是通过别的函数调用了函数自身。但是,如果构成了循环递归调用是非常危险的,但是往往这种情况在代码
复杂的情况下,还是可能发生这种调用。要用代码的规范来避免这种递归调用的发生。
"""
foo()

5>.递归总结

    (1)递归是一种很自然的表达,符合逻辑思维
(2)递归相对运行效率低,每一次调用函数都要开辟栈帧
(3)递归有深度限制,如果递归层次太深,函数反复压栈,栈内存很快就溢出了
(4)如果有限次数的递归,可以使用递归调用,或者使用循环代替,循环代码稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
(5)绝大多数递归,都可以使用循环实现
(6)即使递归代码很简洁,但是能不用则不用递归

四.递归练习

1>.求N的阶乘

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def fac(n):
if n == 1:
return 1
return n * fac(n-1) N = 5 print("{} 的阶乘为:{}".format(N,fac(N))) #以上代码执行结果如下:
5 的阶乘为:120

解法一(推荐使用这种方法,该函数性能最佳,因为时间复杂度是相同的,该解法最简单)

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def fac(n,p = 1):
if n == 1:
return p
p *= n
# print(p)
return fac(n - 1,p) N = 5 print("{} 的阶乘为:{}".format(N,fac(N))) #以上代码执行结果如下:
5 的阶乘为:120

解法二

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def fac(n,p = None):
if p is None:
p = [1]
if n == 1:
return p[0] p[0] *= n
return fac(n - 1,p) N = 5 print("{} 的阶乘为:{}".format(N,fac(N))) #以上代码执行结果如下:
5 的阶乘为:120

解法三

2>.将一个数逆序放入列表中,例如:1234=>【4,3,2,1】

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com data = str(1234) def revert(x):
if x == -1:
return ""
return data[x] + revert(x -1) print(revert(len(data) - 1)) #以上代码执行结果如下:
4321

解法一

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def revert(n,list_1 = None):
if list_1 is None:
list_1 = []
x,y = divmod(n,10)
list_1.append(y) if x == 0:
return list_1
return revert(x,list_1) print(revert(12345)) #以上代码执行结果如下:
[5, 4, 3, 2, 1]

解法二

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com num = 1234 def revert(num,target=[]):
if num:
target.append(num[len(num) - 1]) #等效于target.append(num[-1:])
revert(num[:len(num) - 1])
return target print(revert(str(num))) #以上代码执行结果如下:
['', '', '', '']

解法三

3>.解决猴子吃桃问题

    猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又吃了一个。第二天早上有将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第十天早上想吃时,只剩下一个桃子了。求第一天共摘了多少个桃子。
#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def peach(days = 10):
if days == 1:
return 1
return (peach(days - 1) + 1) * 2 print(peach()) #以上代码执行结果如下:
1534

解法一

#!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def peach(days = 1):
if days == 10:
return 1
return (peach(days + 1) + 1) * 2 print("第一天共摘了{}个桃子".format(peach())) #以上代码执行结果如下:
第一天共摘了1534个桃子

解法二

4>.把字典扁平化

源字典 =  {'a':{'b':,'c':}, 'd':{'e':,'f':{'g':}}}

目标字典 = {'a.c':,'d.e':,'d.f.g':,'a.b':}
 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com source = {'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}} target = {} def flatmap(src,prefix=''):
for k,v in src.items():
if isinstance(v,(list,tuple,set,dict)):
flatmap(v,prefix=prefix + k + '.') #递归调用
else:
target[prefix + k] = v flatmap(source) print(target) #以上代码输出结果如下:
{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}

解法一

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com source = {'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}} def flatmap(src,dest=None,prefix=''):
if dest == None:
dest = {}
for k,v in src.items():
if isinstance(v,(list,tuple,set,dict)):
flatmap(v,dest,prefix=prefix + k + '.') #递归调用
else:
dest[prefix + k] = v return dest target = flatmap(source) print(target) #以上代码输出结果如下:
{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}

解法二

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com source = {'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}} def flatmap(src,dest=None,prefix=''):
def _flatmap(src,dest=None,prefix=''):
for k,v in src.items():
key = prefix + k
if isinstance(v,(list,tuple,set,dict)):
_flatmap(v,dest,key + ".") #递归调用
else:
dest[key] = v
dest = {}
_flatmap(src,dest)
return dest target = flatmap(source) print(target) #以上代码输出结果如下:
{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}

解法三

5>.求2个字符串的最长公共子串

 #!/usr/bin/env python
#_*_coding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
#EMAIL:y1053419035@qq.com def findit(str1,str2):
count = 0
length = len(str1) for sublen in range(length,0,-1):
for start in range(0,length - sublen +1):
substr = str1[start:start + sublen]
count += 1
if str2.find(substr) > -1:
print("count={},substrlen={}0".format(count,sublen))
return substr s1 = "abcdefg"
s2 = "defabcdoabcdeftw"
s3 = "1234a" print(findit(s1,s2))
print(findit(s1,s3)) #以上代码输出结果如下:
count=2,substrlen=60
abcdef
count=22,substrlen=10
a

参考案例

Python入门篇-递归函数Recursion的更多相关文章

  1. Python入门篇-面向对象概述

    Python入门篇-面向对象概述 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.语言的分类 面向机器 抽象成机器指令,机器容易理解 代表:汇编语言 面向过程 做一件事情,排出个 ...

  2. Python入门篇-StringIO和BytesIO

    Python入门篇-StringIO和BytesIO 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.StringIO(用于文本处理) 1>.使用案例 #!/usr/bin ...

  3. Python入门篇-文件操作

    Python入门篇-文件操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.文件IO常用操作 open:打开 read:读取 write:写入 close:关闭 readlin ...

  4. Python入门篇-functools

    Python入门篇-functools 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.reduce方法 reduce方法,顾名思义就是减少 reduce(function,se ...

  5. Python入门篇-类型注解

    Python入门篇-类型注解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.函数定义的弊端 1>.动态语言很灵活,但是这种特性也是弊端 Python是动态语言,变量随时可 ...

  6. Python入门篇-数据结构堆排序Heap Sort

    Python入门篇-数据结构堆排序Heap Sort 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.堆Heap 堆是一个完全二叉树 每个非叶子结点都要大于或者等于其左右孩子结点 ...

  7. Python入门篇-数据结构树(tree)的遍历

    Python入门篇-数据结构树(tree)的遍历 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.遍历 迭代所有元素一遍. 二.树的遍历 对树中所有元素不重复地访问一遍,也称作扫 ...

  8. Python入门篇-装饰器

    Python入门篇-装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.装饰器概述 装饰器(无参) 它是一个函数 函数作为它的形参 返回值也是一个函数 可以使用@functi ...

  9. Python入门篇-高阶函数

    Python入门篇-高阶函数 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.高级函数  1>.First Class Object 函数在Python中是一等公民 函数也 ...

随机推荐

  1. 使用VEGAS2(Versatile Gene-based Association Study)进行gene based的关联分析研究

    gene-based关联分析研究是SNP-based关联分析研究的一个补充. 目前有很多工具支持gene-based关联分析研究,比如GCTA,VEGAS2等. 下面主要介绍一下怎么用VEGAS2做g ...

  2. Nginx打印json日志

    1.修改配置,在http{}中添加 log_format access_json '{"@timestamp":"$time_iso8601",' '" ...

  3. 研发的困境----DEVOPS

    1.研发的困境 互联网的环境 互联网这个环境比较特别,包括现在不只是互联网,就算是被互联网赋能的这些“互联网+”的企业也在改变,用户在发生变化,用户构成的群体在发生变化,群体造成场景的变化,场景营造新 ...

  4. oracle 在列名后的 (+)是什么意思,如何转换为mysql

    外连接的意思select *from a,bwhere a.id=b.id(+)意思就是返回a,b中匹配的行 和 a中有但是b中没有的行. 参考https://www.cnblogs.com/Aaro ...

  5. zuul网关路由作用

    为了方便客户端调用微服务,所以设计出了网关.在微服务实例地址发生改变的情况下,客户端调用服务要能够不受影响. 网关可以完成的功能:路由,反向代理,日志记录,权限控制,限流 在本例子中 Eureka  ...

  6. C++ 每日一题 参数分析 (vector)

    首先给出原题地址: https://www.nowcoder.com/practice/668603dc307e4ef4bb07bcd0615ea677?tpId 以下是代码解析: #include& ...

  7. 大数据之路【第十二篇】:数据挖掘--NLP文本相似度

    一.词频----TF • 假设:如果一个词很重要,应该会在文章中多次出现 • 词频——TF(Term Frequency):一个词在文章中出现的次数 • 也不是绝对的!出现次数最多的是“的”“是”“在 ...

  8. VC++如何利用Matlab2014b的图形引擎进行绘图

    VC++如何利用Matlab的图形引擎 在Visual C++ 2015 工程中使用 Matlab2014b 提供的图形引擎进行绘图的详细过程. 问题来源: 有时候用C++写一些演示程序,有数据可视化 ...

  9. 倾斜动画(SkewTransform)

    Silverlight中的倾斜变化动画(SkewTransform)能够实现对象元素的水平.垂直方向的倾斜变化动画效果.我们现实生活中的倾斜变化效果是非常常见的,比如翻书的纸张效果,关门开门的时候门缝 ...

  10. Linux:检查当前运行级别的五种方法

    运行级就是Linux操作系统当前正在运行的功能级别.存在七个运行级别,编号从0到6.系统可以引导到任何给定的运行级别.运行级别由数字标识. 每个运行级别指定不同的系统配置,并允许访问不同的进程组合.默 ...