《数据结构》详解精炼:递归(1)

第 2 章 算法分析

2.9 递归(1)

1. 递归

递归(英语:Recursion),又译为递回,在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。

例题 2.9.1 阶乘: n ! = 1 × 2 × 3 × ⋯ × n n!=1\times2\times3\times\cdots\times n n!=1×2×3×⋯×n ,并规定 0 ! = 1 0!=1 0!=1 。用递归的方式表示:
f ( n ) = { 1 i f n = 0 n ⋅ f ( n − 1 ) i f n > 0 \begin{split} f(n)=\begin{cases}1&\quad if~n=0\\n\cdot f(n-1)&\quad if ~n\gt0\end{cases} \end{split} f(n)={1n⋅f(n−1)if n=0if n>0

【算法描述】

c 复制代码
long factorial(int n){
    if(n == 0){
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}

在此算法描述中,即使用了递归。

但,使用递归实现阶乘,并不是最优的选择,因为上述所谓递归,可以用循环语句轻易实现。

c 复制代码
long factorial(int n) {
    long fact = 1;
    if(n == 0) return 1;
    for(int i = 1; i <= n; i++) {
        fact *= i;
    }
    return fact;
}

如果程序中出现了条件语句,其时间复杂度的判断应以运行时间长的分支为准,例如在上述任何算法中,显然 if(n == 0) 分支的时间复杂度是 O ( 1 ) O(1) O(1) ,而另外一个分支,时间复杂度是 O ( n ) O(n) O(n) ,故该算法的时间复杂度是 O ( n ) O(n) O(n) 。

例题 2.9.2 斐波那契数列

公元1150年印度数学家 Gopala 和金月在研究箱子包装对象长宽刚好为 1 和 2 的可行方法数目时,首先描述这个数列。在西方,最先研究这个数列的人是比萨的列奥那多(意大利人斐波那契 Leonardo Fibonacci, 1175-1250),他描述兔子生长的数目时用上了这数列:

  • 第一个月初有一对刚诞生的兔子
  • 第二个月之后(第三个月初)它们可以生育
  • 每月每对可生育的兔子会诞生下一对新兔子
  • 兔子永不死去

假设在 n n n 月有兔子总共 a a a 对, n + 1 n + 1 n+1 月总共有 b b b 对。在 n + 2 n + 2 n+2 月必定总共有 a + b a + b a+b 对:因为在 n + 2 n + 2 n+2 月的时候,前一月( n + 1 n + 1 n+1 月)的 b b b 对兔子可以存留至第 n + 2 n + 2 n+2 月(在当月属于新诞生的兔子尚不能生育)。而新生育出的兔子对数等于所有在 n n n 月就已存在的 a a a 对。

斐波纳契数是帕斯卡三角形的每一条红色对角线上数字的和。

用递归方式定义斐波那契数列:
f i b ( n ) = { 1 i f n = 1 f i b ( n − 1 ) + f i b ( n − 2 ) o t h e r s fib(n)=\begin{cases} 1&\quad if ~n=1 \\fib(n-1)+fib(n-2)&\quad~others \end{cases} fib(n)={1fib(n−1)+fib(n−2)if n=1 others

【算法描述】

c 复制代码
long fib(long n){
    if(n == 1 || n == 2) return 1;
    else return fib(n-1) + fib(n-2);
}

对斐波那契数列算法的时间复杂度分析,后面会单独介绍。

上面两个例子中显示,用递归实现算法,必须要确保有穷性,这也是算法的特性之一(参考 006 节),为此,必须有递归终止的条件,例如斐波那契数列的算法描述中的第 2 行。终止条件处的结果应该是直接算出来,而不依靠递归的。这是递归的基本法则之一。

递归的基本法则:

  1. 基准情形(base case):必须要有某些基准的情形,不用递归就能求解,也就是递归的终止条件。
  2. 不断推进(making progress):对于那些需要递归求解的情形,递归调用必须总能朝着产生基准情形的方向推进。
  3. 设计法则:假设所有的递归调用都能运行。
  4. 合成效益法则(compound interest rule):在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作

对于斐波那契数列的递归算法描述,就违背了第 4 点,后续专门进行时间复杂度分析的时候会详解。

本文由mdnice多平台发布

相关推荐
demo007x13 小时前
让大模型活在你的鼠标旁:我用 Tauri 2 + Rust 打造了一款“反直觉”的 AI 全局划词效率神器
macos·程序员·llm
程序员海军14 小时前
AI 越来越强,为什么打工人反而越来越累、越来越内耗了?
前端·程序员·aigc
badhope18 小时前
同样写 AI,为什么有的文章 549 人看、有的只有 16 人看?——我用 40 篇掘金文章的真实数据复盘
程序员
今朝唯我少年郎2 天前
Codex安全盲区代码漏洞生成实测
python·程序员
kyriewen2 天前
我用 AI 写完一个需求后才发现,最难的不是 prompt,而是验收
前端·程序员·ai编程
酷酷的逗逗乐2 天前
前端转 Agent 开发 · 第六节
前端·程序员
爱勇宝2 天前
没有 Fn 键关触控板?我做了一个双击即用的 Windows 小工具
前端·后端·程序员
程序员cxuan3 天前
跟 WebUI 说再见了,最强 DeepSeek 桌面端来了!
人工智能·后端·程序员
codigger3 天前
用 AI 写代码,你知道它会把我的代码传去哪?
ai·程序员·编程·数据安全
程序员cxuan3 天前
DeepSeek V4.1 Flash 正式发布!
人工智能·后端·程序员