第七章:递推与递归 (Recursion & Recurrence)
递推与递归是算法世界中一对密不可分的概念,它们是解决复杂问题、简化代码逻辑的强大武器。本章将带你深入理解其核心思想,并掌握在C++中高效实现它们的方法。
7.1 思想:自己调用自己,从已知推未知
想象一下你想知道你前面排了多少人,但你只能问你前面的那个人。于是你问他:“你前面有多少人?” 他为了回答你,又去问他前面的人,如此循环,直到问到队首的那个人。队首的人知道自己前面是0个人(这就是边界),然后他把答案告诉后面的人,后面的人再根据这个答案计算出自己的位置,一层层地传回给你。
这个过程就是“递归”,而贯穿其中的数学关系——你的位置 = (你前面那个人的位置) + 1——就是“递推”。
-
递推 (Recurrence): 是一种数学思想,指的是一个问题的解可以通过一个或多个规模更小的相同问题的解来表示。这个数学关系式被称为递推关系式。例如,斐波那契数列的递推关系是 F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2)F(n)=F(n−1)+F(n−2)。
-
递归 (Recursion): 是一种编程技巧,指的是一个函数在它的定义中直接或间接地调用自身。它将递推关系式直接翻译成了代码。
一个正确的递归程序必须包含两个核心要素:
- 基本情况 (Base Case): 也叫递归出口或边界条件。这是问题的最简形式,可以直接求解,无需再次递归。就像排队问题中的队首,它是递归的终点。如果缺少基本情况,函数将无限调用自身,直到耗尽所有资源(栈溢出),导致程序崩溃(Runtime Error)。
- 递归步骤 (Recursive Step): 将原问题分解成一个或多个规模更小、但结构相同的子问题,并调用自身来解决这些子问题。就像你问前面的人,这是一个将问题规模“递”减的过程。
递推是“从已知推未知”的思维模型,而递归是实现这种模型的一种优雅代码形式。
7.2 C++实现:函数递归调用与边界条件
在C++中,实现递归就是编写一个会调用自己的函数。理解函数调用栈是理解递归的关键。每当一个函数被调用,系统会在内存的“栈”区为它分配一块空间(称为栈帧),用于存储它的局部变量、参数和返回地址。当函数返回时,这个栈帧被销毁。
在递归调用中,每一次函数调用都会创建一个新的栈帧。如果递归深度太深,栈空间会被耗尽,这就是所谓的“栈溢出”。
下面是一个递归函数的通用C++结构:
// 返回类型 函数名(参数列表)
return_type recursive_function(parameters) {
// 1. 基本情况 (Base Case) / 边界条件
// 这是递归的出口,必须有!
if (满足边界条件) {
return 某个确定的值;
}
// 2. 递归步骤 (Recursive Step)
// a. 对数据进行处理,使其更接近边界
// b. 调用自身,解决规模更小的子问题
return_type sub_result = recursive_function(规模更小的参数);
// c. 利用子问题的解,构造原问题的解
return final_result;
}
关键: 必须确保每次递归调用的参数都在向“边界条件”靠近,否则递归将永不停止。
7.3 经典例题:斐波那E契数列、汉诺塔、全排列
例题1:斐波那契数列 (Fibonacci Sequence)
斐波那契数列的定义是 F(0)=0,F(1)=1F(0)=0, F(1)=1F(0)=0,F(1)=1,当 n 1n \> 1n1 时,F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2)F(n)=F(n−1)+F(n−

最低0.47元/天 解锁文章
643

被折叠的 条评论
为什么被折叠?



