Python入门篇-递归函数(recursion)
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.递归概述
(1)函数直接或者间接调用自身就是递归; (2)递归需要有边界,递归前进段,递归返回段; (3)递归一定要有边界条件; (4)当边界条件不满足的时候,递归前进; (5)当边界条件满足的时候,递归返回;
二.递归案例
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 import sys 8 9 print("默认的递归调用次数限制0是:{} 次".format(sys.getrecursionlimit()))10 11 sys.setrecursionlimit(20000) #我们修改默认的递归调用次数12 13 print("当前递归调用次数限制是:{} 次".format(sys.getrecursionlimit()))14 """15 递归要求:16 递归一定要有退出条件,递归调用一定要执行到这个退出条件。没有退出条件的递归调用,就是无限调用。17 递归调用的深度不宜过深:18 Python对递归调用的深度做了限制,以保护解释器19 超过递归深度限制,抛出:"RecursionError: maxinum recursion depth exceeded" 即超过了最大递归深度20 sys.getrecursionlimit()21 """22 23 24 25 """26 斐波拉契数字推理公式:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)27 """28 def fib(n):29 return 1 if n < 2 else fib(n-1) + fib(n-2)30 31 for i in range(20):32 print(fib(i), end=' ')33 34 35 #以上代码执行结果如下:36 默认的递归调用次数限制0是:1000 次37 当前递归调用次数限制是:20000 次38 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
三.递归的性能
1>.使用for循环打印斐波拉契前35个数字
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 8 import datetime 9 10 start = datetime.datetime.now()11 12 pre = 013 14 cur = 1 # No115 16 print(pre, cur, end=' ')17 18 n = 3519 20 for i in range(n-1):21 pre, cur = cur, pre + cur22 print(cur, end=' ')23 24 delta = (datetime.datetime.now() - start).total_seconds()25 26 print("\n递归调用打印斐波拉契钱35个数字的时间为:{}".format(delta))27 28 29 30 #以上代码直接结果如下:31 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 32 递归调用打印斐波拉契钱35个数字的时间为:0.0
2>.使用递归方式打印斐波拉契前35个数字
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 import datetime 8 9 n = 3510 11 start = datetime.datetime.now()12 13 def fib(n):14 return 1 if n < 2 else fib(n-1) + fib(n-2)15 16 for i in range(n):17 print(fib(i), end=' ')18 19 delta = (datetime.datetime.now() -start).total_seconds()20 21 print("\n递归调用打印斐波拉契钱35个数字的时间为:{}".format(delta))22 23 24 25 #以上代码直接结果如下:26 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 27 递归调用打印斐波拉契钱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.comimport datetimestart = datetime.datetime.now()pre = 0cur = 1 # No1print(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.comdef 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的阶乘
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdef fac(n): if n == 1: return 1 return n * fac(n-1)N = 5print("{} 的阶乘为:{}".format(N,fac(N)))#以上代码执行结果如下:5 的阶乘为:120
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdef fac(n,p = 1): if n == 1: return p p *= n # print(p) return fac(n - 1,p)N = 5print("{} 的阶乘为:{}".format(N,fac(N)))#以上代码执行结果如下:5 的阶乘为:120
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdef 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 = 5print("{} 的阶乘为:{}".format(N,fac(N)))#以上代码执行结果如下:5 的阶乘为:120
2>.将一个数逆序放入列表中,例如:1234=>【4,3,2,1】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdata = str(1234)def revert(x): if x == -1: return "" return data[x] + revert(x -1)print(revert(len(data) - 1))#以上代码执行结果如下:4321
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdef 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]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comnum = 1234def revert(num,target=[]): if num: target.append(num[len(num) - 1]) #等效于target.append(num[-1:]) revert(num[:len(num) - 1]) return targetprint(revert(str(num)))#以上代码执行结果如下:['4', '3', '2', '1']
3>.解决猴子吃桃问题
猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又吃了一个。第二天早上有将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第十天早上想吃时,只剩下一个桃子了。求第一天共摘了多少个桃子。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdef peach(days = 10): if days == 1: return 1 return (peach(days - 1) + 1) * 2print(peach())#以上代码执行结果如下:1534
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#!/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.comdef peach(days = 1): if days == 10: return 1 return (peach(days + 1) + 1) * 2print("第一天共摘了{}个桃子".format(peach()))#以上代码执行结果如下:第一天共摘了1534个桃子
4>.把字典扁平化
源字典 = { 'a':{ 'b':1,'c':2}, 'd':{ 'e':3,'f':{ 'g':4}}}目标字典 = { 'a.c':2,'d.e':3,'d.f.g':4,'a.b':1}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 8 source = { 'a':{ 'b':1,'c':2},'d':{ 'e':3,'f':{ 'g':4}}} 9 10 target = {}11 12 13 def flatmap(src,prefix=''):14 for k,v in src.items():15 if isinstance(v,(list,tuple,set,dict)):16 flatmap(v,prefix=prefix + k + '.') #递归调用17 else:18 target[prefix + k] = v19 20 flatmap(source)21 22 print(target)23 24 25 26 #以上代码输出结果如下:27 { 'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 8 source = { 'a':{ 'b':1,'c':2},'d':{ 'e':3,'f':{ 'g':4}}} 9 10 11 def flatmap(src,dest=None,prefix=''):12 if dest == None:13 dest = {}14 for k,v in src.items():15 if isinstance(v,(list,tuple,set,dict)):16 flatmap(v,dest,prefix=prefix + k + '.') #递归调用17 else:18 dest[prefix + k] = v19 20 return dest21 22 target = flatmap(source)23 24 print(target)25 26 27 28 #以上代码输出结果如下:29 { 'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 8 source = { 'a':{ 'b':1,'c':2},'d':{ 'e':3,'f':{ 'g':4}}} 9 10 11 def flatmap(src,dest=None,prefix=''):12 def _flatmap(src,dest=None,prefix=''):13 for k,v in src.items():14 key = prefix + k15 if isinstance(v,(list,tuple,set,dict)):16 _flatmap(v,dest,key + ".") #递归调用17 else:18 dest[key] = v19 dest = {}20 _flatmap(src,dest)21 return dest22 23 target = flatmap(source)24 25 print(target)26 27 28 29 #以上代码输出结果如下:30 { 'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
5>.实现Base64编码(要求自己实现算法,不用库)
索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y
|
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 8 import base64 9 10 s1 = "abcdfg"11 12 print(base64.b64encode(s1.encode()))13 14 15 #以上代码输出结果如下:16 b'YWJjZGZn'
6>.求2个字符串的最长公共子串
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #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/ 5 #EMAIL:y1053419035@qq.com 6 7 8 def findit(str1,str2): 9 count = 010 length = len(str1)11 12 for sublen in range(length,0,-1):13 for start in range(0,length - sublen +1):14 substr = str1[start:start + sublen]15 count += 116 if str2.find(substr) > -1:17 print("count={},substrlen={}0".format(count,sublen))18 return substr19 20 s1 = "abcdefg"21 s2 = "defabcdoabcdeftw"22 s3 = "1234a"23 24 print(findit(s1,s2))25 print(findit(s1,s3))26 27 28 29 #以上代码输出结果如下:30 count=2,substrlen=6031 abcdef32 count=22,substrlen=1033 a