数据结构与算法——7. 递归:递归的概念与实现

文章目录

  • 一、递归( Recursion)的概念
    • 1. 递归的简单应用:数列求和
      • 问题
      • 使用循环算法解决
      • 使用递归算法解决
    • 2. 递归程序的执行过程
    • 3. 递归三定律
  • 二、递归调用的实现
    • 1. 递归与栈的关系
    • 2. Python对递归深度(递归调用次数)的限制

一、递归( Recursion)的概念

递归是一种解决问题的方法,其精髓在于将问题分解为规模更小的相同问题,持续分解,直到问题规模小到可以用非常简单直接的方式来解决。

递归算法最显著的特征就是:在算法流程中调用自身

1. 递归的简单应用:数列求和

问题

给定一个列表,返回所有数的和。

使用循环算法解决

def list_sum(num_list):
    the_sum = 0
    for i in num_list:
        the_sum = the_sum + i
       return the_sum

使用递归算法解决

  • 分解过程:

    假设列表为[1, 3, 5, 7, 9]。我们把列表求和这个大问题分解为两个数相加的小问题,如同下面这样(注意括号的添加):

    t o t a l = 1 + ( 3 + 5 + 7 + 9 ) total= 1+(3+5+7+9) total=1+(3+5+7+9)

    t o t a l = 1 + ( 3 + ( 5 + 7 + 9 ) ) total= 1+(3+(5+7+9)) total=1+(3+(5+7+9))

    t o t a l = 1 + ( 3 + ( 5 + ( 7 + 9 ) ) ) total= 1+(3+(5+(7+9))) total=1+(3+(5+(7+9)))

    t o t a l = 1 + ( 3 + ( 5 + ( 7 + ( 9 ) ) ) ) total= 1+(3+(5+(7+(9)))) total=1+(3+(5+(7+(9))))

    在最后一次分解后中,括号内只剩下了一个9,就没必要继续分解下去了,我们的分解过程就算完成了。

    我们将上面的分解过程归纳一下,可以得到这样一个公式:数列的和=“首个数”+“余下数列”。而“余下数列”的和依旧可以使用这个公式来计算,这样一直分解,直到不能分解为止。

  • 求和过程:

    分解完成后,我们的计算就十分简单了。只需要对每一对括号内的数两两相加,最后就能得到整个数列的和:

    t o t a l = ( 1 + ( 3 + ( 5 + ( 7 + 9 ) ) ) ) total= (1+(3+(5+(7+9)))) total=(1+(3+(5+(7+9))))

    t o t a l = ( 1 + ( 3 + ( 5 + 16 ) ) ) total= (1+(3+(5+16))) total=(1+(3+(5+16)))

    t o t a l = ( 1 + ( 3 + 21 ) ) total= (1+(3+21)) total=(1+(3+21))

    t o t a l = ( 1 + 24 ) total= (1+24) total=(1+24)

    t o t a l = 25 total= 25 total=25

  • 代码实现:

    def list_sum(num_list):
        # 只剩一个元素,直接返回
       if len(num_list) == 1:
            return num_list[0]
        # 否则,计算“首个数”+“余下数列”
       else:
            return num_list[0] + list_sum(num_list[1:])  # 调用自身,传入“余下数列”
    

2. 递归程序的执行过程

递归程序分为调用返回两个阶段,先不停的调用自身(分解问题),之后一层层地将结果返回(计算答案):

调用阶段(从上往下读):

数据结构与算法——7. 递归:递归的概念与实现_第1张图片

返回阶段(从下往上读):

数据结构与算法——7. 递归:递归的概念与实现_第2张图片

3. 递归三定律

  1. 递归算法必须有一个基本结束条件,这个条件也叫作递归出口(最小规模的问题直接解决)。

    比如,上面数列求和问题中,当数列分解到只剩一个元素的时候,直接返回该元素,停止后续分解。

  2. 递归算法必须能改变状态向基本结束条件演进(问题规模逐渐减小)。

    比如,上面数列求和问题中,数列会被分解的越来越小,数列大小慢慢向“1”靠近,而数列大小为“1”,就是该算法的基本结束条件。

  3. 递归算法必须调用自身(使用自身解决规模减小后的相同问题)。

二、递归调用的实现

1. 递归与栈的关系

由递归程序的执行过程,我们得知递归程序的调用是一层层向下的,而返回过程则恰好相反,一层层向上。

换个说法:最先一次的函数调用在最后返回,而最后一次的函数调用则是最先返回。这就跟栈的“后进先出”次序是一样的。因此,在实现递归调用的时候,通常就会使用栈来保存每一次调用的现场数据

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

2. Python对递归深度(递归调用次数)的限制

python对递归深度是有限制的,因为系统调用栈容量有限。超出限制会抛出RecursionError异常。所以,一定不要忘记设置递归出口!!!

python默认的递归深度为1000,我们可以通过内置的sys模块获取和调整最大递归深度:

import sys
# 获取
sys.getrecursionlimit()  # 返回:1000

# 设置
sys.setrecursionlimit(3000)

你可能感兴趣的:(数据结构与算法,算法,数据结构,递归算法)