数据结构之递归入门

递归是解决问题的一种方法,它将问题不断地分成更小的子问题,直到子问题可以用普通的方法解决。通常情况下,递归会使用一个不停调用自己的函数。尽管表面上看起来很普通,但是递归可以帮助我们写出非常优雅的解决方案。对于某些问题,如果不用递归,就很难解决。

我们从一个简单的问题开始学习递归。即使不用递归,我们也知道如何解决这个问题。

假设需要计算数字列表[1, 3, 5, 7, 9]的和。
不用递归的写法如下

def listsum(numList):
    theSum=0
    for i in numList:
        theSum=theSum+i
    return theSum

print(listsum([1,3,5,7,9]))

假设暂时没有while循环和for循环。应该如何计算结果呢?如果你是数学家,就会记得加法是接受两个参数(一对数)的函数。将问题从求一列数之和重新定义成求数字对之和,可以将数字列表重写成完全括号表达式,例如((((1 + 3) + 5) + 7) +9)。该表达式还有另一种添加括号的方式,即(1 + (3 + (5 + (7 + 9))))。注意,最内层的括号对(7 + 9)不用循环或者其他特殊语法结构就能直接求解。事实上,可以使用下面的简化步骤来求总和。

总和 = (1 + (3 + (5 + (7 + 9))))
总和 = (1 + (3 + (5 + 16)))
总和 = (1 + (3 + 21))
总和 = (1 + 24)
总和 = 25

如何将上述想法转换成Python程序呢?让我们用Python列表来重新表述求和问题。数字列表numList的总和等于列表中的第一个元素(numList[0])加上其余元素(numList[1:])之和。可以用函数的形式来表述这个定义。listSum(numList) = first(numList) + listSum(rest(numList))first(numList)返回列表中的第一个元素,rest(numList)则返回其余元素。用Python可以轻松地实现这个等式

def listsum(numList):
    if len(numList) == 1:
        return numList[0]
    else:
        return numList[0] + listsum(numList[1:])

print(listsum([1, 3, 5, 7, 9]))  # 25

在这一段代码中,有两个重要的思想值得探讨。首先,第2行检查列表是否只包含一个元素。这个检查非常重要,同时也是该函数的退出语句。对于长度为1的列表,其元素之和就是列表中的数。其次,listsum函数在第5行调用了自己!这就是我们将listsum称为递归函数的原因——递归函数会调用自己

图4-1展示了在求解[1, 3, 5, 7, 9]之和时的一系列递归调用。我们需要将这一系列调用看作一系列简化操作。每一次递归调用都是在解决一个更小的问题,如此进行下去,直到问题本身不能再简化为止。
在这里插入图片描述
当问题无法再简化时,我们开始拼接所有子问题的答案,以此解决最初的问题。图4-2展示了listsum函数在返回一系列调用的结果时进行的加法操作。当它返回到顶层时,就有了最终答案。
在这里插入图片描述

递归三原则

正如阿西莫夫提出的机器人三原则一样,所有的递归算法都要遵守三个重要的原则:

(1) 递归算法必须有基本情况;
(2) 递归算法必须改变其状态并向基本情况靠近;
(3) 递归算法必须递归地调用自己。

让我们来看看listsum算法是如何遵守上述原则的。基本情况是指使算法停止递归的条件,这通常是小到足以直接解决的问题。listsum算法的基本情况就是列表的长度为1。

为了遵守第二条原则,必须设法改变算法的状态,从而使其向基本情况靠近。改变状态是指修改算法所用的某些数据,这通常意味着代表问题的数据以某种方式变得更小。listsum算法的主数据结构是一个列表,因此必须改变该列表的状态。由于基本情况是列表的长度为1,因此向基本情况靠近的做法自然就是缩短列表。这正是代码清单4-2的第5行所做的,即在一个更短的列表上调用listsum。

最后一条原则是递归算法必须对自身进行调用,这正是递归的定义。对于很多新手程序员来说,递归是令他们颇感困惑的概念。新手程序员知道如何将一个大问题分解成众多小问题,并通过编写函数来解决每一个小问题。然而,递归似乎让他们落入怪圈:有一个需要用函数来解决的问题,但是这个函数通过调用自己来解决问题。其实,递归的逻辑并不是循环,而是将问题分解成更小、更容易解决的子问题

总结

  • 循环均可以用递归实现
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值