一、递归的思想
- 递归分为递推和回归两个过程
以求一个数n的n!为例:
递推:若路人A能帮我把(n-1)!求出来,那我只需要在此基础上乘n就行了 路人A会想,若路人B能帮我把(n-2)!求出来,那我只需要在此基础上乘(n-1)就行了。
照这样执行下去,总有一个人的工作是只需要把1!求出来就行了,那这个非常简单,我们定义一下1!=1就可以了。
回归:当有人把1!=1算出来之后,就要开始算2!了,然后开始3!,知道n!,这个过程就称为回归。
- 递归不能无休止地执行下去,要有一个结束的标志
如:
def recursion():
recursion()
这种递归是毫无意义的,因为缺少递归完成的条件。等编译器执行到设置最大递归次数后就会出现异常。
在上一个例子中,定义1!=1就是递归完成的条件。
二、递归的几个例子
- 求数n的阶乘n!
首先在没有学习递归之前我们可以使用迭代的方法去完成,代码如下:
def fact(n):
result = n
for i in range(1,n):
result *= i
return result
number = int(input('请输入一个数:'))
result = fact(number)
print("%d的阶乘为: %d"%(number,result))
学习了递归可以使用递归方法,代码如下:
def fact_recursion(n):
if n == 1:
return 1
return n*fact_recursion(n-1)
number = int(input('请输入一个数:'))
result = fact_recursion(number)
print("%d的阶乘为: %d"%(number,result)
2. 求菲波那切数列
注意在斐波那契数列中用到了两个自我调用,所以会需要两个初始值(或者称为完成条件),即:f(1)=f(2)=1
代码如下:
def febnaqie(n):
if n == 1 or n == 2:
return 1
return febnaqie(n-1)+febnaqie(n-2)
for i in range(1,13):
print(febnaqie(i))
实际上递归的实现时非常浪费时间的,当递归层数逐渐变大时,计算速度所需时间会变得很长,而迭代会非常省时:
def fibb(n):
a,b = 0,1
for i in range(1,n+1):
a,b =b,a+b #注意此处采用的这种赋值法,是先进行右边计算在进行赋值,不能用a = b 然后再b = a+b
print(a)
fibb(12)
1
1
2
3
5
8
13
21
34
55
89
144
3. 求解汉诺塔问题
代码如下:
def hanoi(n,x,y,z):#x为起始位置(柱子),y为借用的柱子,z为目的位置
if n == 1:
print(x,'-->',z)#结束的条件,也就是递推到最后一步时最后一个人做的工作
else:
hanoi(n-1,x,z,y)#第一步,有人将n-1个圆盘从x借助z移动到y柱子上
print(x,'-->',z)#第二步,我将最后一个圆盘从x柱子移动到z柱子上
hanoi(n-1,y,x,z)#第三步,有人将n-1个圆盘从y柱子借助x柱子移动到z柱子上
hanoi(3,'x','y','z')
注意代码中的第一步第三步均使用自身定义的函数完成。
执行效果展示如下:
三、递归和迭代的区别
观察上面那段 求数n的阶乘n! 的代码,我们发现:
迭代:使用的是循环结构;不需要调用函数和占用额外内存
递归:使用的是选择结构;能使程序的结构更清晰、简介,更容易让人读懂;缺点是耗费运行时间和内存
四、计算机是如何实现递归的
计算机在执行递归时分为前行和退回两个阶段,退回顺序是它前行顺序的逆序。在退回过程中,可能要执行某些动作,包括恢复在前行过程中存储起来的某些数据。
这种存储和恢复方式,显然符合栈这种数据结构。因此编译器使用栈实现递归操作
要想真正理解递归,上面这段话非常重要!!!
递归在不断判断的过程其实是一个压栈的过程,当条件判断结束时,计算机会自动执行出栈,将之前计算得到的结果一个个拿出来用于计算。