1.递归?
理解递归
递归就是将问题分解为规模更小的相同问题。 就像下图的存钱罐一样,不断不断变小~~ 持续分解,直到可以用非常简单直接的方式解决问题。
明显特征:在运行过程中调用自己
递归算法三定律
1.递归算法必须要有基本结束条件,否则就会无限循环。 2.递归算法必须能改变状态向基本结束条件演进 3.递归算法必须调用自身
2.简单应用:
2.1数列求和
问题
给定一个列表,返回所有数的和(不使用循环语句)
举例:[1,3,5,7,9] 返回:25
思考
数列求和?如果这个数列只有一个数,那求和的结果就是它自己了。所以
if len(numList)==1:
return numlist[0]
如果我有更多数呢? 1+3?可以看成是 1(首项)+3(最后一项) 1+3+5?可以看成 1(首项)+(3(首项)+5(最后一项)) 1+3+5+7?可以看成 1(首项)+(3(首项)+(5(首项)+7(最后一项))) 也就是 数列的和等于首个数+余下数列的和
函数调用和返回的链条:
代码
def listsum(numList):
if len(numList)==1:
return numList[0]
else:
return numlist[0]+listsum(numList[1:])
print(listsum([1,3,5,7,9]))
2.2任意进制转换
问题
编写程序,实现从二进制到十六进制的任意转换
思考
我们用最熟悉的十进制来思考这个问题:
把十进制数转为十进制数?应该怎么做?这是进制转换中最简单的情况。
十进制有十个不同符号∶ convString ="0123456789" 比十小的整数,转换成十进制,直接查表就可以了∶ convString[n] 所以,基本结束条件就是小于十的整数
if n<base:
return convertString[n]
想办法把比十大的整数,拆成一系列比十小的整数,逐个查表。比如七百六十九,拆成七、六、九,查表得到 769就可以了。
我们用整数除,求余数两个计算来将整数一步步拆开 除以"进制基base"(// base) 对"进制基"求余数(% base)
进制基:十进制基是10,八进制基是8,二进制基是2,十六进制基是16
余数总小于"进制基base",是"基本结束条件",可直接进行查表转换。整数商成为"更小规模"问题,通过递归调用自身解决 。
代码
def toStr(n,base):
convertString="0123456789ABCDEF"
if n<base:
return convertString[n]
else:
return toStr(n//base,base)+converString[n%base]
3.递归调用怎么实现的?
当一个函数被调用的时候,系统会把调用时的现场数据压入到系统调用栈每次调用,压入栈的现场数据称为栈帧 当函数返回时,要从调用栈的栈顶取得返回地址恢复现场,弹出栈帧,按地址返回。
因为数据会压入栈,而栈也有一定的容量,所以递归有固定的深度,当数据太多超过了递归的深度时,就会报错!
如何解决? 1.检查是否设置了基本结束条件 2.优化算法 3.调整深度 在Python内置的sys模块可以获取和调整最大递归深度
>>> import sys
>>> sys.getrecursionlimit()
>1000
>>>sys.setrecursionlimit(3000)
>>> sys.getrecursionlimit()
> 3000
4.复杂应用
4.1汉诺塔
传说故事
汉诺塔问题是法国数学家Edouard Lucas于 1883年,根据传说提出来的。
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。
并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 汉诺塔游戏规则如下:
1、有三根相邻的柱子,标号为A,B,C。 2、A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。 3、现在把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。
思考
假设我们有5个盘子,穿在1#柱,需要挪到3#柱
如果能有办法把最上面的一摞4个盘子统统挪到 2#柱,那问题就好解决了∶ 把剩下的最大号盘子直接从1#柱挪到3#柱 再用同样的办法把2#柱上的那一摞4个盘子挪到 3#柱,就完成了整个移动。 接下来的问题就是四个盘子如何从1挪到2?
同样是想办法把上面的一摞3个盘子挪到3#柱,把剩下最大号盘子从1#挪到2#柱。再用同样的办法把一摞3个盘子从3#挪到2#柱。 (手画的有点丑哈哈哈哈)
一摞3个盘子的挪动也照此∶ 如何把三个盘子从3#移到2#? 分为上面一摞2个,和下面最大号盘子 那么2个盘子怎么移动? 如何把两个盘子从1#挪到2#? 那就再分解为1个盘子的移动! 仔细看这个挪动的过程,感觉都差不多??? 不就是 将盘片塔从开始柱,经由中间柱,移动到目标柱∶ 首先将上层N-1个盘片的盘片塔,从开始柱,经由目标柱,移动到中间柱; 然后将第N个(最大的)盘片,从开始柱,移动到目标柱; 最后将放置在中间柱的N-1个盘片的盘片塔,经由开始柱,移动到目标柱。 基本结束条件,也就是最小规模问题是∶ 1个盘片的移动问题
代码
def moveTower(height,fromPole,withPole,toPole):
if height >= 1:
moveTower(height-1,fromPole,toPole,withPole)
moveDisk(height,fromPole,toPole)
moveTower(height-1,withPole,fromPole,toPole)
def moveDisk(disk,fromPole,toPole):
print(f"Moving disk[{disk}] from {fromPole} to {toPole}")
moveTower(3,"#1","#2","#3")
|