GESP5级C++考试语法知识(十一、递归算法(一))

🌟 第一章:什么是递归?("套娃小精灵"的故事)

1、🎯 想象一个魔法世界:

有一个小精灵,它不会做复杂的事情,但它有一个绝招:

👉 遇到问题,就把问题变成一个"更小的同样问题",然后再让自己去做!

这就是------递归!


2、🧠 递归的本质

👉 一个函数调用自己

就像这样:

复制代码
void f()
{
    f(); // 自己调用自己
}

⚠️ 但这样会无限循环!

所以必须有:


3、🎯 递归三大要素

1️⃣ 终止条件(出口)

👉 不然就会无限调用(爆炸💥)

2️⃣ 递归公式(规律)

👉 大问题 = 小问题

3️⃣ 缩小规模

👉 每次问题都变小


🌟 第二章:经典故事------爬楼梯

1、🎯 故事:

(1)小明要爬楼梯,每次可以:

  • 走1步

  • 或走2步


(2)问:爬到第 n 阶有多少种方法?


2、🧠 思考过程

(1)假设:

  • f(n) 表示到第 n 阶的方法数

(2)🎯 分情况:

到第 n 阶,最后一步可能是:

  • 从 n-1 走1步

  • 从 n-2 走2步


(3)👉 所以:

复制代码
f(n) = f(n-1) + f(n-2)

3、🎯 终止条件:

复制代码
f(1) = 1
f(2) = 2

4、💻 C++代码

复制代码
int f(int n)
{
    if(n == 1) return 1;
    if(n == 2) return 2;

    return f(n-1) + f(n-2);
}

5、🌲 调用过程(像树一样)

算 f(5):

复制代码
f(5)
├── f(4)
│   ├── f(3)
│   │   ├── f(2)
│   │   └── f(1)
│   └── f(2)
└── f(3)
    ├── f(2)
    └── f(1)

6、👉 会发现:很多重复计算!


🌟 第三章:递归的时间复杂度

1、🎯 结论(先记住)

对于上面的代码:

👉 时间复杂度是:

复制代码
O(2^n)

2、🧠 为什么?

(1)因为:

👉 每次都会分成两条路

像一棵"爆炸树"🌳:

复制代码
        f(n)
       /   \
   f(n-1)  f(n-2)
    /  \      ...

(2)👉 节点数量 ≈ 2^n


(3)🌟 小结一句话:

👉 递归越分叉,时间越慢!


🌟 第四章:递归的空间复杂度

1、🎯 空间来自哪里?

👉 来自:函数调用栈


2、🧠 想象:

函数调用像叠盘子 🍽:

复制代码
f(5)
↓
f(4)
↓
f(3)
↓
f(2)
↓
f(1)

👉 最深:n 层


3、🎯 结论:

👉 空间复杂度:

复制代码
O(n)

🌟 第五章:递归的三大优化策略🔥


🚀 优化1:记忆化(避免重复计算)

🎯 思路:

👉 算过的结果存起来!


💻 C++实现

复制代码
int dp[100];

int f(int n)
{
    if(n == 1) return 1;
    if(n == 2) return 2;

    if(dp[n]) return dp[n];

    return dp[n] = f(n-1) + f(n-2);
}

🎯 效果:

👉 时间复杂度:

复制代码
O(n)

💥 从指数 → 线性!


🚀 优化2:改成循环(递推)

🎯 思路:

👉 既然是从小推大,那就不用递归!


💻 代码

复制代码
int f(int n)
{
    int a = 1, b = 2;

    for(int i = 3; i <= n; i++)
    {
        int c = a + b;
        a = b;
        b = c;
    }

    return b;
}

🎯 优势:

  • 不会爆栈

  • 更快

  • 更省空间

👉 空间复杂度:O(1)


🚀 优化3:剪枝(减少不必要计算)

🎯 用在搜索问题(DFS)

比如:

👉 找路径

👉 走迷宫


🧠 思想:

👉 "这条路不可能成功,就别走了!"


💻 示例

复制代码
void dfs(int step)
{
    if(step > 10) return; // 剪枝

    // 继续搜索
}

🌟 第六章:什么时候用递归?

🎯 看到这些关键词,可能想到递归:

  • 树 🌳(二叉树、DFS)

  • 分治(归并排序)

  • 回溯(全排列)

  • 搜索(迷宫)


🌟 最终总结

👉 递归就像一个聪明的小精灵:

1️⃣ 不会做大问题

👉 就把问题变小

2️⃣ 一直变小

👉 直到能解决

3️⃣ 再把答案一点点拼回来


🎯 一句话记住递归:

👉 "大问题 = 小问题 + 自己"


🌟 课后练习

试试写:

1️⃣ 求 n 的阶乘

2️⃣ 斐波那契数列

3️⃣ 打印 1~n


接下来我们继续学习,递归的记忆化搜索!


🌟 第一章:为什么需要记忆化?("健忘的小精灵")

1、🎯 故事:

还是那个递归小精灵 🧚‍♂️

它很努力,但有个大问题:

👉 它每次都会忘记自己算过什么!


2、比如它在算:

复制代码
f(5)

结果它:

  • 算了一次 f(3)

  • 又算了一次 f(3)

  • 又又算了一次 f(3)...😵

👉 就像一个人写作业,每题都重新推一遍!


3、🌲 重复计算图

复制代码
f(5)
├── f(4)
│   ├── f(3)
│   │   ├── f(2)
│   │   └── f(1)
│   └── f(2)
└── f(3)
    ├── f(2)
    └── f(1)

👉 f(3) 被算了很多次!


🌟 第二章:记忆化搜索的核心思想

1、🎯 一句话:

👉 算过的结果,存起来,下次直接用!


2、🧠 类比:

像这样:

🧑‍🎓 小朋友做题:

  • 第一次:认真算 ✅

  • 第二次:直接抄答案 😎


🌟 第三章:经典实例(爬楼梯升级版)

1、🎯 问题:

还是爬楼梯:

👉 每次走 1 或 2 步

👉 求到第 n 阶的方法数


2、❌ 普通递归(慢)

复制代码
int f(int n)
{
    if(n == 1) return 1;
    if(n == 2) return 2;

    return f(n-1) + f(n-2);
}

3、👉 时间复杂度:O(2^n)(爆炸💥)


🌟 第四章:加入记忆化(核心!)


1、🎯 第一步:准备一个"记忆本"

复制代码
int dp[100]; // 初始为0

👉 dp[n] = f(n)


2、🎯 第二步:查询是否算过

复制代码
if(dp[n] != 0) return dp[n];

3、🎯 第三步:算完要存!

复制代码
dp[n] = f(n-1) + f(n-2);

4、💻 完整代码

复制代码
#include <iostream>
using namespace std;

int dp[100];

int f(int n)
{
    if(n == 1) return 1;
    if(n == 2) return 2;

    if(dp[n] != 0) return dp[n]; // 查表

    dp[n] = f(n-1) + f(n-2);     // 计算并存

    return dp[n];
}

int main()
{
    cout << f(10) << endl;
}

🌟 第五章:复杂度变化(超级重要!)


1、🎯 没有记忆化:

👉 每个状态重复计算

👉 时间复杂度:

复制代码
O(2^n)

2、🎯 有记忆化:

👉 每个 n 只算一次!

👉 时间复杂度:

复制代码
O(n)

💥 直接从"爆炸级"变"线性级"!


3、🎯 空间复杂度:

  • dp数组:O(n)

  • 递归栈:O(n)

👉 总空间:O(n)


🌟 第六章:再来一个更强例子🔥(背包问题雏形)


1、🎯 问题:

有 n 个物品,每个物品可以选或不选

👉 问:有多少种选法?


2、🧠 递归思路:

复制代码
f(i) = f(i-1) + f(i-1)

👉 选 or 不选


3、❌ 问题:

👉 同样会重复计算!


4、🌟 记忆化版本


💻 C++代码

复制代码
int dp[100];

int f(int n)
{
    if(n == 0) return 1;

    if(dp[n]) return dp[n];

    return dp[n] = f(n-1) + f(n-1);
}

5、🎯 本质:

👉 把"树形递归"压缩成"线性计算"


🌟 第七章:记忆化搜索模板(必须掌握!)


🧩 通用模板

复制代码
int dp[100]; // 初始化为0

int dfs(int x)
{
    // 1️⃣ 终止条件
    if(边界) return 值;

    // 2️⃣ 查缓存
    if(dp[x] != 0) return dp[x];

    // 3️⃣ 递归计算
    dp[x] = dfs(子问题);

    // 4️⃣ 返回结果
    return dp[x];
}

🌟 第八章:什么时候用记忆化?

看到这些情况就用👇


1、🎯 特征

✅ 有"重复子问题"

✅ 是递归

✅ 可以用数组存状态


2、🎯 常见题型

  • 斐波那契数列

  • 爬楼梯

  • 背包问题

  • 树形DP

  • DFS + 状态


🌟 第九章:递归 vs 记忆化 vs 动态规划


方法 本质 速度
递归 暴力
记忆化 递归 + 存储
DP 迭代 更快

👉 关系:

💡 记忆化搜索 = 自顶向下的动态规划


🌟 最终总结

👉 记忆化搜索就是:

复制代码
算过就记住,下次直接用!

🎯 一句话理解:

👉 "递归 + 小本本 = 高级算法!"


相关推荐
是娇娇公主~2 小时前
C++迭代器详解
开发语言·c++·stl
qq_148115372 小时前
C++网络编程(Boost.Asio)
开发语言·c++·算法
2301_804215412 小时前
内存映射文件高级用法
开发语言·c++·算法
ZHENGZJM2 小时前
负载均衡式在线评测系统(Load-Balanced Online OJ)技术全景指南
c++·负载均衡·软件工程·idea
CoderCodingNo2 小时前
【GESP】C++八级考试大纲知识点梳理 (5) 代数与平面几何
开发语言·c++
爱喝白开水a2 小时前
春节后普通程序员如何“丝滑”跨行AI:不啃算法,也能拿走AI
java·人工智能·算法·spring·ai·前端框架·大模型
张辰宇-3 小时前
AcWing 5 多重背包问题 II
算法
zhangren024683 小时前
PHP vs C++:从Web脚本到系统编程的终极对比
开发语言·c++·php
小则又沐风a3 小时前
类和对象(C++)---上
java·c++·算法