1. 概念:什么是递归?

递归(Recursion)是一种解决问题的方法。尤其是复杂问题,有时用递归解决复杂问题可能会出奇的简单。

递归将一个比较复杂的问题分解成更小规模的问题,持续分解直到问题规模小到可以用非常简单的方式直接解决。

分解在算法上的表现出来的就是:在算法流程中调用自身。

2. 举例说明递归:数列求和

问题:给定一个列表,返回所有数的和,列表中数的个数不定。

2.1 用循环的解法:

需要一个循环和一个累加变量来迭代求和。

def sum_list(alist):sum_of_list = 0for i in alist:sum_of_list = sum_of_list + ireturn sum_of_listprint(sum_list([1, 3, 5, 7, 9]))

2.2 如果不使用while或for的话,还能求解吗?

数列求和,其实是由很多次的两个数相加实现的。我们需要做的,是如何将规模大的数列求和分解为规模小的两数求和。同样属于求和问题,只是规模发生了变化,这就是递归解决问题的特征。

对[1, 3, 5, 7, 9]这个数列进行求和,除了循环,还有另外一种方式:全括号表达式(1 + (3 + (5 + (7 + 9))))
最内层的 (7 + 9) 直接相加就可以计算的出。
然后 (5 + (7 + 9)) 就可以用 (5 + 16) 直接计算出。
整个求和的过程就是:

整个问题就可以归纳成: 数列和 = “首个数” + “余下数列和” 当余下数列只有一个数时,它的“数列和”就是它本身。此时,规模就已经小到了可以直接解决。

sum_list(alist)=alist[0]+sum_list(alist[1:])sum\_list(alist) = alist[0] + sum \_ list(alist[1: ])sum_list(alist)=alist[0]+sum_list(alist[1:])

写成代码就是:

def sum_list(alist):# 问题的最小规模,直接输出if len(alist) == 1:return alist[0]else:# 调用函数自身,减小函数的规模return alist[0] + sum_list(alist[1:])print(sum_list([1, 3, 5, 7, 9]))

这个程序的重点在于:

  1. 给出了最小规模的条件,可以直接解决最小问题。
  2. 具备分解问题的步骤,通过调用函数自身减小问题的规模。

函数的执行和返回过程如下图。

2.3 递归三定律

1,递归算法必须有一个基本结束条件(最小规模问题的直接解决)
2,递归算法必须能改变状态向基本结束条件演进(减小问题规模)
3,递归算法必须调用自身(解决减小了规模的相同问题)

对于上例的数列求和问题,对应的三定律分别为:
1,数列求和问题首先具备了基本结束条件:当列表长度为1的时候,直接输出所包含的唯一数
2,数列求和处理的数据对象是一个列表,而基本结束条件是长度为1的列表,那递归算法就要改变列表并向长度为1的状态演进。我们看到其具体做法是将列表长度减少1
3,调用自身是递归算法中最难理解的部分,实际上我们理解为“问题分解成了规模更小的相同问题”就可以了在数列求和算法中就是“更短数列的求和问题

2.4 简易高效理解

如果你还没懂的话,问题不大,因为我看完了也是懵的。

对于递归有没有什么好的理解方法? - 方应杭的回答 - 知乎
https://www.zhihu.com/question/31412436/answer/738989709

这篇文章能更帮助理解。

3. 递归的应用

3.1 十进制整数转换为任意进制

之前曾用栈实现过进制的转换,这里,采用递归实现。递归和栈具有一定的关系。递归要解决的是从十进制到二进制、四进制、八进制、十六进制等进制的任意进制转换。

首先来看最熟悉的十进制,即十进制整数转换为十进制。

十进制包含10个符号:conv_string = “0123456789”

对于小于10的十进制整数,如“5”,直接查表就行了
conv_string[5]
因此本题的基本结束条件就是一位的整数小于基(即进制数,十进制的基就是10,十六进制的基就是16),直接查表并输出。

对于大于10的十进制整数,如“769”,延续上面的方法,把“769”拆分成多个小于“10”的整数就行了,即拆分成 “7” , “6”, “9”,然后将这个三个小字符串拼接即可。现在问题就在于拆分的方法。

拆分的目的在于每次从多位的整数中分离出一位小于基整数,对于769,拆分的过程就是:“769” = “76” + “9” , “76” = “7” + “6” 。在分离的过程中,这个多位的整数位数朝着变成一位整数的方向发展,这就是本体的想基本结束条件演进(减小规模)

拆分可以同时使用求余数和整数除,求余数用于分离出大整数的最后一位,整数除用于保留除最后一位外的前面几位。
具体来讲,整数除是“十进制整数 // 基base”。
求余数“十进制整数 % 基base”。

例如:
769 % 10 = 9 < 10 -> “9”
769 // 10 = 76

76 % 10 = 6 < 10 -> “6”
76 // 10 = 7 < 10 -> “7”

整数就是 “7” + “6” + “9” = “769”

基本结束条件就是余数小于10。
减小规模就是整数除后的商位数小于原整数。
调用自身就是每次对整数商使用自身函数减少规模。

流程图如下:

类比到十进制转为二进制,十进制转为十六进制也是一样的。
769 // 2 = 384
769 % 2 = 1 < 2 -> 1
384 // 2 = 192
384 % 2 = 0 < 2 -> 0
192 // 2 = 96
192 % 2 = 0 < 2 -> 0
96 // 2 = 48
96 % 2 = 0 < 2 -> 0
48 // 2 = 24
48 % 2 = 0 < 2 -> 0
24 // 2 = 12
24 % 2 = 0 < 2 -> 0
12 // 2 = 6
12 % 2 = 0 < 2 -> 0
6 // 2 = 3
6 % 2 = 0 < 2 -> 0
3 // 2 = 1 < 2 -> 1
3 % 2 = 1 < 2 -> 1
从下往上把字符串拼接起来
所以 769 转成 二进制 就是 1100000001

代码如下:

def to_str(number, base):convert_str = "0123456789ABCDEF"if number < base: # 最小结束条件return convert_str[number]else:return to_str(number // base, base) + convert_str[number % base]  # 向最小结束条件演化print(to_str(769, 2))
print(to_str(769, 10))
print(to_str(769, 16))

3.2 求阶乘

直接给出代码吧。

def fac(n):if n != 1:result = n * fac(n - 1)  # 向基本结束条件 n = 1 演进else:  # 基本结束条件,当1的阶乘等于1result = 1return resultprint(fac(6))

4. 递归

调用在系统中是怎么实现的

4.1 递归实现的原理

当一个函数被调用的时候,系统会把调用时的现场数据压入到系统调用栈。
每次调用,压入栈的现场数据称为栈帧。
当函数返回时,要从调用栈的栈顶取得返回地址,恢复现场,弹出栈帧,按地址返回。

意思是最外层的第一次调用被压入栈底,当他前面的那些操作(即里层的自身函数)都弄完了,才轮到它。而最内层的符合最小结束条件的是第一个解决并将结果返回的,以保证后面的操作可以顺利执行。

4.2 Python中的递归深度限制

在调用递归算法的程序时,会遇到这样的错误:RecursionError。如下图。

这是因为递归的层数太多,而系统调用栈容量 有限。

解决方法:
要检查程序中是否忘记设置基本结束条件,导致无限递归。
或者向基本结束条件演进太慢,导致递归层数太多,调用栈溢出。

Python的内置的sys模块可以在Python内置的sys模块可以获取和调整最大递归深度。

递归的影视作品:
前目的地
恐怖游轮

参考文献

本文的知识来源于B站视频 【慕课+课堂实录】数据结构与算法Python版-北京大学-陈斌-字幕校对-【完结!】,是对陈斌老师课程的复习总结

Python数据结构14:递归的原理,递归实现数列求和、求阶乘、十进制转为任意进制相关推荐

  1. Python使用栈十进制转二进制、十进制转任意进制(代码)

    # 10转2 from pythonds.basic import Stack # 引入栈def divide_by2(dec_number):rem_stack = Stack()while dec ...

  2. 数据结构---递归实现十进制装换为任意进制

    数据结构-递归实现十进制装换为任意进制 代码: #include <stdio.h> #include <stdlib.h> #include"stack.h&quo ...

  3. 任意进制转换的递归实现

    任意进制转换的递归实现 原始链接:http://weolee.spaces.live.com/blog/cns!52FBB34F02E029D9!650.entry www.163online.cn ...

  4. 使用递归将十进制转换成任意进制(Number Conversion)

    关于递归 什么是递归? 说白了,就是函数自己调用自己,然后被调用的函数继续调用自己,这将无限循环下去,除非代码中有终止调用链的的内容. 解释一下递归的所经历的过程 递归需要有边界条件.递归前进段和递归 ...

  5. [进制转换]用python实现10进制转任意进制的方法

    目录 问题引出: 原理:10进制数转任意进制数都是通过除以目标进制数取余得到的. 具体步骤: 结果展示: 代码实现: ①数值转换预处理 实现原理 ②得到目标进制数 创建字典的原理 问题引出: 各种算术 ...

  6. python编写程序转换进制_Python实现任意进制之间的转换

    在日常生活中我们频繁使用到数学的进制,如季度逢三进一,星期逢七进一:生肖.小时使用12进制,每天使用24进制,每月使用30进制,分秒使用60进制,一年使用360进制等等:在编程过程中我们经常需要转换进 ...

  7. python任意进制转换界面_python:整数转换为任意进制(

    javascript提供了一个number.toString(baseNum)的函数,能够将number转换为36进制以下的字符串. python里面仅提供了将字符串转换为整数的函数,并没有提供相应的 ...

  8. python进制转化器(任意进制)

    目标:实现任意进制转换(2-36进制) 先输入一个当前进制与目标进制,下一行输入当前进制的数,回车即可得出目标进制数 方法: 将当前进制数利用 int函数 转化为十进制 n = int(str(c), ...

  9. 数据结构 study 7: 栈 实现 十进制 转换为 8进制

    数据结构 study 6: 栈 实现 十进制 转换为 8进制 先用文字和图片 ,描述清楚这个问题. 然后自己通过代码实现. 代码纯手工写. 文字描述 达到 只要看完文字描述,就知道怎么写代码 题目描述 ...

最新文章

  1. msbuild FileSysExcludeFiles
  2. IDEA小技巧:Debug条件断点
  3. 深度学习练手项目(一)-----利用PyTorch实现MNIST手写数字识别
  4. windows数据自动上传服务器,windows服务器间自动传输文件
  5. 开源框架 Drupal 修复多个访问绕过和 CSRF 漏洞
  6. Linux下 vim代码编译器的使用
  7. 4符号代码_ELF文件格式解析器 原理 + 代码
  8. AE “每用户订阅上的所有人SID 不存在”
  9. uniapp使用高德地图
  10. 四、随机变量及其分布函数的基本定义和性质 random variables and distribution
  11. python 等值面多边形平滑_三次贝塞尔曲线平滑多边形
  12. Netty03-进阶
  13. Word 2013 设置封面不含页码
  14. [转载]使用Java生成带有下划线字体的文字
  15. python获取网页数据后写入mysql_HTTP协议与使用Python获取数据并写入MySQL
  16. 国惠光电红外相机二次开发中常见问题
  17. USACO Spinning Wheels
  18. hive中字段中含有反斜杠 用like 出现问题
  19. 任正非:华为是世界上最穷的高科技公司,比任何一个公司都穷
  20. 小学生蓝桥杯Python闯关 | 吉利又霸气的英文名

热门文章

  1. 【技巧】Latex在线工具:公式编辑器、表格编辑器
  2. 腾讯云存储产品介绍第三章-云存储服务
  3. css 去掉li 标签列表项前面的点
  4. 多旋翼无人机仿真 rotors_simulator 是如何悬停的(一)
  5. 芯片分享第三弹——MS2109
  6. dom 兄弟_DOM技巧和技巧:父母,孩子和兄弟姐妹
  7. 怎样才能生活的愉快——学会与人沟通
  8. 安卓ucweb(非ucmobile)去广告
  9. Mac Hadoop2.6(CDH5.9.2)伪分布式集群安装
  10. 牛客白月赛19【题解】