递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

递归函数特性:

  1. 必须有一个明确的结束条件;
  2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  3. 相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。
  4. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)最后,如果你的时间不是很紧张,并且又想快速的提高,最重要的是不怕吃苦,建议你可以联系:mengy7762 ,那个真的很不错,很多人进步都很快,需要你不怕吃苦哦!大家可以去添加上看一下~

先举个简单的例子:计算1到100之间相加之和;通过循环和递归两种方式实现

#!/usr/bin/env python3# 1-100 sumimport sysdef sum_cycle(n): ''' 1 to n,The sum function ''' sum = 0 for i in range(1,n + 1): sum += i return sumdef sum_recu(n): ''' 1 to n,The sum function ''' if n > 0: return n + sum_recu(n - 1)    #调用函数自身 else: return 0print("循环求和:",sum_cycle(100))print("递归求和:",sum_recu(100))执行结果:[root@localhost tmp]# ./sum.py循环求和: 5050递归求和: 5050

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

***使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

把上面的递归求和函数的参数改成10000就导致栈溢出!

[root@localhost tmp]# ./sum.py循环求和: 5050Traceback (most recent call last): File "./sum.py", line 23, in  print("递归求和:",sum_recu(1000)) File "./sum.py", line 19, in sum_recu return n + sum_recu(n - 1) File "./sum.py", line 19, in sum_recu return n + sum_recu(n - 1) File "./sum.py", line 19, in sum_recu return n + sum_recu(n - 1) [Previous line repeated 994 more times] File "./sum.py", line 18, in sum_recu if n > 0:RecursionError: maximum recursion depth exceeded in comparison

***解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归优化:http://www.open-open.com/lib/view/open1480494663229.html

二分法查找大家应该听说过;就是一种快速查找的方法,时间复杂度低,逻辑简单易懂,总的来说就是不断的找出中间值,用中间值对比你需要找的实际值;若中间值大,则继续找左边;若中间值小,则继续找右边;可以看出二分法就是不断重复此上过程,所以就可以通过递归方式来实现二分法查找了!

今天在 codewar 做题目玩,做到一题时,想试试用递归写,却发现在函数体内一切正常,可是打印返回值时,打印出来的结果却是 None 。不明白是在哪里除了问题了,于是在网上翻了一圈,自己又仔细思考了下整个逻辑。似乎弄清楚了。这里我来解释下我的理解。

首先,我来写一个简化的递归函数,来做用于解释的案例。

def ex(x): if x > 1: x -= 1 ex(x) else: return xt = ex(5)print(t)

上面的程序逻辑超简单:判断输入的书是否大于 1 ,如果大于 1 则把它减 1 ,然后再判断。如此不断递归下去。当 x 不大于 1 的时候,返回 x 的大小。

很容易认为返回的 x 打印出来的结果是 1 是不?但是不是的。你可以把上面的代码运行下,打印出来的结果是:

None

为什么是这样的呢?我的理解是:

当我们在 t = ex(5) 的时候,调用了一次函数。如果函数直接返回 t = 1,那是木有问题的。问题是,程序在这里返回数据。反而又去调用了一次函数本身。这次调用的的是 ex(4) 然后一直调用的 ex(1) 这时候程序终于运行到了 return 这里。那这次 return 给了谁呢?答案是谁都没有给。因为我们没有设置 t = ex(1), 所以函数的 return 回的数据就这么丢失了。最后,如果你的时间不是很紧张,并且又想快速的提高,最重要的是不怕吃苦,建议你可以联系:mengy7762 ,那个真的很不错,很多人进步都很快,需要你不怕吃苦哦!大家可以去添加上看一下~

而我们调用的 t = ex(5) 呢,因为它的 if > 1 是成立的,所以直接跳过了了 else: return x ,等于这一次调用,是没有用到 return 的。而在 Python 中规定,如果一个函数体没有 return 的时候,那它的 return 就是 None 。这也就是为什么,我们运行上面的程序的时候,得到的结果是 None 。

为了证明这点,我们可以去写一个程序,去尝试获得迭代到 ex(1) 这次调用时 ex(1) 所返回的值。程序如下:

t = {}def ex(x): global t if x > 1: x -= 1 t[x] = ex(x) else: return xex(10)print(t)

在程序中,我们用字典,把 ex(x) 每次调用所返回的数据记录了下来。结果如下:

{1: 1, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}

可见:

  1. 在迭代中,后调用的函数先返回。
  2. 除了最后一次调用的函数有了正确的返回外,之前的函数都没有 return ,所以返回的都是 None。

那如何正确地返回迭代出来的数据呢?答案是:一层一层第把最后一次调用返回出来。代码如下:

def ex(x): if x > 1: x -= 1 return ex(x) else: return xt = ex(5)print(t)

结果为:

1

为什么这次能正确呢?因为第一次调用 ex(x) 的时候,它返回的是ex(x-1),下一次调用返回了 ex(x-2) 一路携手交传,最终把 ex(1) return 给了 t ,这个时候就等于 t = ex(1) 了,自然就得到了正确的结果了。

嗯,本文就到这里要结束了。最后说一下, Python 的确不喜欢迭代。所以它严格限制了迭代的次数。我电脑上,只要迭代的次数超过 999 ,就会报错,如下:

t = ex(998)

1

t = ex(999)

Traceback (most recent call last):File "E:\Work\Coder\Test\test4.py", line 14, in t = ex(999)RecursionError: maximum recursion depth exceeded in comparison

***解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归优化:http://www.open-open.com/lib/view/open1480494663229.html

二分法查找大家应该听说过;就是一种快速查找的方法,时间复杂度低,逻辑简单易懂,总的来说就是不断的找出中间值,用中间值对比你需要找的实际值;若中间值大,则继续找左边;若中间值小,则继续找右边;可以看出二分法就是不断重复此上过程,所以就可以通过递归方式来实现二分法查找了!

#!/usr/bin/env python3#The binary search functiondef Binary_Search(data_source,find_n): if len(data_source) >= 1: #判断列表长度是否大于1,小于1就是一个值 mid = int(len(data_source)/2) #获取列表中间索引;奇数长度列表长度除以2会得到小数,通过int将转换整型 if find_n > data_source[-1]: #判断查找值是否超出最大值 print('{}查找值不存在!'.format(find_n)) exit() elif find_n < data_source[0]: #判断查找值是否超出最小值 print('{}查找值不存在!'.format(find_n)) exit() if data_source[mid] > find_n: #判断列表中间值是否大于查找值 #print('查找值在 {} 左边'.format(data_source[mid])) Binary_Search(data_source[:mid],find_n) #调用自己,并将中间值左边所有元素做参数 elif data_source[mid] < find_n: #判断列表中间值是否小于查找值 #print('查找值在 {} 右边'.format(data_source[mid])) #调用自己,并将中间值右边所有元素做参数 Binary_Search(data_source[mid:],find_n) else: print('找到查找值',data_source[mid]) #找到查找值 else: print('{}查找值不存在!'.format(find_n)) #特殊情况,返回查找不到Data = [22,12,41,99,101,323,1009,232,887,97]Data.sort() #列表从小到大排序Binary_Search(Data,323) #查找323执行结果:[root@localhost tmp]# ./binary_search.py 找到查找值 323

好了,今天的知识就分享到这里,欢迎关注python教程入门学习,私信关键词:学习资料,获取更多学习资源,如果文章对你有有帮助,请收藏关注,在今后与你分享更多学习python的文章。同时欢迎在下面评论区留言如何学习python。最后,如果你的时间不是很紧张,并且又想快速的提高,最重要的是不怕吃苦,建议你可以联系:mengy7762 ,那个真的很不错,很多人进步都很快,需要你不怕吃苦哦!大家可以去添加上看一下~

详细解读Python 递归函数!相关推荐

  1. python方法_详细解读Python中的__init__()方法

    __init__()方法意义重大的原因有两个.第一个原因是在对象生命周期中初始化是最重要的一步:每个对象必须正确初始化后才能正常工作.第二个原因是__init__()参数值可以有多种形式. 因为有很多 ...

  2. python中def _init_是什么意思_详细解读Python中的__init__()方法

    __init__()方法意义重大的原因有两个.第一个原因是在对象生命周期中初始化是最重要的一步:每个对象必须正确初始化后才能正常工作.第二个原因是__init__()参数值可以有多种形式. 因为有很多 ...

  3. 聚类分析详细解读python

    文章目录 相似性测度 1.距离测度 1.1 欧式距离 1.2 街坊距离(Manhattan距离) 1.3切式(Chebyshev)距离 1.4明氏(Minkowski)距离 1.5 马氏(Mahala ...

  4. 线性规划单纯形法python实现与代码详细解读

    线性规划单纯形法python实现与代码详细解读 1 单纯形法(Simplex method) 2 编程思路 3 python实现原理解读 4 python代码 5 后记 1 单纯形法(Simplex ...

  5. MemCache超详细解读

    MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高 ...

  6. MemCache详细解读

    MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高 ...

  7. 刚刚,OpenStack 第 19 个版本来了,附28项特性详细解读!

    刚刚,OpenStack 第 19 个版本来了,附28项特性详细解读! OpenStack Stein版本引入了新的多云编排功能,以及帮助实现边缘计算用例的增强功能. OpenStack由一系列相互关 ...

  8. python函数教程:Python递归函数 二分查找算法实现解析

    这篇文章主要介绍了Python递归函数 二分查找算法实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.初始递归 递归函数:在一个函数里在调 ...

  9. 【带你看看开源圈的新趋势】GITHUB OCTOVERSE 2022 详细解读

    一.写在前面 GITHUB OCTOVERSE 是由世界上最大的代码托管平台 GITHUB 发起的开源趋势调查,旨在探索开源的新趋势,以及对于开发者和软件公司的影响.通过分析 GITHUB OCTOV ...

最新文章

  1. PHP中的PathInfo
  2. SoapUI实践:自动化测试、压力测试、持续集成 1
  3. 如何在企业内部实现云信私有化
  4. leaflet大量marker卡顿_leaflet如何加载10万数据
  5. 如何提高用户注册转化与用户激活
  6. 部门选择控件源代码公布
  7. MySQL 批量生成 SQL 脚本语句解决实际的业务需求/如何拼接字符串/拼接字符串的 SQL 语句
  8. 两种方法求解 正数数组中 两个数相减 的最大值
  9. Cookie跨域的问题解决方案
  10. oracle安装总结
  11. GDB+coredump定位段错误
  12. Java8 Stream 流使用场景和常用操作
  13. Python官方文档中文版在线教程
  14. 在Windows 7上面安装Vs2005
  15. 海外版华为手机安装谷歌框架(也适用于国内任意机型)
  16. [HNOI2007]紧急疏散evacuate
  17. Oracle 10.2.0.3使用Logminor工具和把system表空间变成locally
  18. CDH安装指南——酒仙网技术
  19. Zeev Suraski、Andi Gutmans、Rasmus Lerdorf
  20. curl http header_利用Linux命令cURL实现文件定时上传到ftp服务器的小程序,实用

热门文章

  1. 【linux 001】【利用staging Driver】【001】wifi AR6000 驱动移植引发的 staging 的问题
  2. twitter java_Twitter算法面试题详解(Java实现)
  3. 苹果手机免密支付怎么取消_超市“扫码枪”有啥神奇功能?直接扫走你的资金,不用输入密码|移动支付|手机|免密...
  4. vhdl和c语言,VHDL语言中的信号、变量与常量异同比较(转)
  5. php apache停止工作,phpStudy Apache 经常出现停止工作
  6. 内点惩罚函数法matlab_拉格朗日乘数法求解多元条件极值问题
  7. 石头剪刀布程序流程图_“剪刀、石头、布”也能救命?临安区社会福利中心开展海姆立克急救法培训...
  8. python和gui结合实例_Python GUI —— 简单例子+讲解
  9. 自注意力机制_与SENet互补提升,华为诺亚提出自注意力新机制:Weight ExcitationECCV2020...
  10. 服务器显示配置命令,linux查看服务器配置命令