Python中 递归函数 的理解

2022.4.3
此文章重度参考以下文章
递归函数的讲解
递归函数深入浅出
递归函数再讲
重新思考递归函数
首先一定要明确:
递归函数的定义 : 自己调用自己的函数就是递归
递: 去 归: 回 一去一回就是递归

递归有两大特点:
1.反复执行的过程(调用自身)
2.结束反复执行过程的条件(方法跳出点)

递归的设计
  一个递归调用可以导致更多的递归调用,因为这个方法继续把每个子问题分解成新的子问题,要终止一个递归方法,问题最后必须达到一个终止条件。当问题达到这个终止条件时,就将结果返回给调用者。然后调用者进行计算并将结果返回给自己的调用者。这个过程持续进行,直到结果传给原始的调用者位为止。

递归的设计通常包含两部分
1.递归的定义:将大问题转化为小问题求解。
  递归定义就是对问题分解,将一个问题分解为规模较小的问题并用相同的程序去解决。

2.递归的终止条件:跳出递归,返回最小问题的解。
  递归终止条件通常就是得出最小问题的解,并返回给他的调用者。

使用递归解决问题
所有的递归程序都具有一下特点:
1.这些方法使用if-else或switch语句来引导不同的情况
2.一个或多个基础情况(最简单的情况)用来终止递归。
3.每次递归都会简化原始问题,让它不断接近基础情况,直到它变成基础情况(最小问题)为止。
  
通常,要使用递归解决问题,就要将这个问题分解为子问题。每个子问题几乎与原始问题一样的,只是规模小一些。可以应用相同的方法(程序)来递归解决子问题。

递归与迭代的区别:
递归(recursion):递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。(A调用A)

迭代(iteration):重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。(A重复调用B)

递归是一个树结构,从字面可以其理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”,其过程相当于树的深度优先遍历。

迭代是一个环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态。
可以如此理解:
递归是由最终要求的结果一步步回推到最开始最简单的情况,而最开始最简单的情况的答案是已知的。

迭代是从最开始的情况开始,不断重复某一运算过程从而计算出最终要求的结果。

递归的特点与需要注意的点:
1.递归是一去一回的过程,
调用函数时,会开辟栈帧空间,函数执行结束之后,会释放栈帧空间
递归实际上就是不停的开辟和释放栈帧空间的过程
每次开辟栈帧空间,都是独立的一份,其中的资源不共享

2.触发回的过程
1.当最后一层栈帧空间全部执行结束的时候,会触底反弹,回到上一层空间的调用处
2.遇到return,会触底反弹,回到上一层空间的调用处,

3.写递归时,必须给与递归跳出的条件,否则会发生内存溢出,蓝屏死机的情况.
如果递归层数过多,不推荐使用递归

举个例子来理解:

# 1.一个简单的递归例子
def digui(n):
    print(n,"<====1===>")
    if n > 0:    
        digui(n-1)
    print(n,"<====2===>")
digui(5)

"""
# 代码解析:
去的过程:
n = 5 print(5,"<====1===>") 5>0 条件成立-> digui(5-1) => digui(4) 代码阻塞在第13行
n = 4 print(4,"<====1===>") 4>0 条件成立-> digui(4-1) => digui(3) 代码阻塞在第13行
n = 3 print(3,"<====1===>") 3>0 条件成立-> digui(3-1) => digui(2) 代码阻塞在第13行
n = 2 print(2,"<====1===>") 2>0 条件成立-> digui(2-1) => digui(1) 代码阻塞在第13行
n = 1 print(1,"<====1===>") 1>0 条件成立-> digui(1-1) => digui(0) 代码阻塞在第13行
n = 0 print(0,"<====1===>") 0>0 条件不成立 print(0,"<====2===>") 
当前这层空间代码已经执行结束
此刻触发回的过程

n = 1 从上一次13行的代码阻塞位置,继续向下执行 print(1,"<====2===>")
n = 2 从上一次13行的代码阻塞位置,继续向下执行 print(2,"<====2===>")
n = 3 从上一次13行的代码阻塞位置,继续向下执行 print(3,"<====2===>")
n = 4 从上一次13行的代码阻塞位置,继续向下执行 print(4,"<====2===>")
n = 5 从上一次13行的代码阻塞位置,继续向下执行 print(5,"<====2===>")
到此,递归函数彻底执行结束.
5 4 3 2 1 0 0 1 2 3 4 5

递归练习:
练习1:用递归函数计算阶乘

# 常规方法
# 5! = 5*4*3*2*1
def func(n):
    total = 1
    for i in range(n,0,-1):
        total *= i
    return total

res = func(5)
print(res)

# 递归写法
def jiecheng(n):
    if n <= 1:
        return 1
    return n*jiecheng(n-1)
res = jiecheng(5)
print(res)
"""
return 后面的表达式,一定是先计算完在返回


# 代码解析:
# 去的过程:
n = 5   return 5*jiecheng(5-1) => 5 * jiecheng(4)
n = 4   return 4*jiecheng(4-1) => 4 * jiecheng(3)
n = 3   return 3*jiecheng(3-1) => 3 * jiecheng(2)
n = 2   return 2*jiecheng(2-1) => 2 * jiecheng(1)
n = 1   return 1

# 回的过程:
n = 2   return 2*jiecheng(2-1) => 2 * jiecheng(1) => 2 * 1
n = 3   return 3*jiecheng(3-1) => 3 * jiecheng(2) => 3 * 2 * 1
n = 4   return 4*jiecheng(4-1) => 4 * jiecheng(3) => 4 * 3 * 2 * 1
n = 5   return 5*jiecheng(5-1) => 5 * jiecheng(4) => 5 * 4 * 3 * 2 * 1
return 5 * 4 * 3 * 2 * 1 => return 120

额外解析:
jiecheng(1) => 1
jiecheng(2) => 2*jiecheng(1) => 2*1
jiecheng(3) => 3*jiecheng(2) => 3*2*1
jiecheng(4) => 4*jiecheng(3) => 4*3*2*1
jiecheng(5) => 5*jiecheng(4) => 5*4*3*2*1

练习2:尾递归函数
自己调用自己,并且非表达式
计算的结果要在参数当中完成.
尾递归无论调用多少次函数,都只占用一份空间,但是目前cpython不支持.

def jiecheng(n,endval):
    if n <= 1:
        return endval
    return jiecheng(n-1,endval*n)

res = jiecheng(5,1)
print(res)



"""
# 代码解析:
去的过程
n=5 , endval=1         return jiecheng(5-1,endval*5) => jiecheng(4,1*5)
n=4 , endval=1*5       return jiecheng(4-1,endval*4) => jiecheng(3,1*5*4)
n=3 , endval=1*5*4     return jiecheng(3-1,endval*3) => jiecheng(2,1*5*4*3)
n=2 , endval=1*5*4*3   return jiecheng(2-1,endval*2) => jiecheng(1,1*5*4*3*2)
n=1 , endval=1*5*4*3*2 return 120

回的过程:
n=2 return 120
n=3 return 120
n=4 return 120
n=5 return 120

因为最后一层空间的返回值就是第一层空间的返回值,所有在使用尾递归的时候
不需要考虑回的逻辑过程,就能解决问题.推荐使用.

可以对尾递归计算n的阶乘例子进行优化,因为在jiecheng()函数中,有两个参数,这样用户有可能会乱传参数,为了防止乱传参数。设计了如下两种方法解决:

# 优化1
def jiecheng(n,endval=1):
    if n <= 1:
        return endval
    return jiecheng(n-1,endval*n)

res = jiecheng(5)
print(res,"<111>")


# 优化2
"""为了避免用户乱传参数,把endval这个参数隐藏起来"""
def outer(n):
    def jiecheng(n,endval=1):
        if n <= 1:
            return endval
        return jiecheng(n-1,endval*n)
        
    return jiecheng(n) # jiecheng(n-1,endval*n)

res = outer(5)
print(res)

练习3:递归函数计算斐波那契数列

def feb(n):
    # 递归跳出的条件
    if n <= 2: # n == 1 or n == 2 => 1
        return 1
    return feb(n-1) + feb(n-2)
res = feb(5)
print(res)
  • 12
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值