Chap1 绪论与算法分析
返回:0300 数据结构总目录 · 下一章:0302 Chap2 向量
1、学习目标
本章先回答一个问题:为什么同样能得到结果的程序,仍然要比较“好坏”。数据结构后面每一章都会反复讨论时间、空间、最坏情况、平均情况和分摊意义,所以绪论不是开场白,而是整门课的计量工具。
| 模块 | 要掌握的内容 |
|---|---|
| 计算 | 算法是有限、确定、可执行的步骤序列 |
| 模型 | RAM 模型、基本操作、输入规模 |
| 复杂度 | 大 O、Omega、Theta、常见增长阶 |
| 分析 | 级数估计、循环分析、递归分析 |
| 方法 | 迭代、递归、减而治之、分而治之、动态规划 |
| 下界 | 排序下界、比较决策树、问题本身的限制 |
2、资料范围
| PDF 课件 | 内容 |
|---|---|
01.Introduction.A.Computation.pdf |
计算、算法、正确性 |
01.Introduction.B.Computational_Models.pdf |
计算模型、RAM、基本操作 |
01.Introduction.C.Big_o.pdf |
大 O 记号和增长阶 |
01.Introduction.D.Algorithm_analysis.pdf |
复杂度估计和常见级数 |
01.Introduction.E.Iteration_Recursion.pdf |
迭代与递归 |
01.Introduction.F.Dynamic_Programming.pdf |
动态规划入口 |
01.Introduction.X1.Limitation.pdf |
算法和计算的限制 |
01.Introduction.X2.Sorting_Lower_Bound.pdf |
排序问题下界 |
3、核心概念
3.1 算法
算法是一组有限步骤,通常要求:
| 性质 | 说明 |
|---|---|
| 有穷性 | 不能无限执行 |
| 确定性 | 每一步含义明确 |
| 可行性 | 每一步能机械执行 |
| 输入 | 有零个或多个输入 |
| 输出 | 至少产生一个结果 |
数据结构课程里,算法不是孤立存在的。一个操作的复杂度往往取决于底层结构:顺序表插入可能移动大量元素,链表插入可以改指针,但查找又没有随机访问。
3.2 计算模型
常用 RAM 模型把以下操作近似视为常数时间:
1 | -*/%、赋值、比较、数组下标访问、指针读写、函数基本跳转 |
这个模型不是硬件真实细节,而是为了比较算法增长趋势。真正写项目时还要考虑缓存、分支预测、内存分配等工程因素;但在数据结构课里,先把输入规模 n 和基本操作次数说清楚。
4、复杂度记号
| 记号 | 含义 | 直觉 |
|---|---|---|
O(f(n)) |
渐进上界 | 不会比 f(n) 增长得更快 |
Ω(f(n)) |
渐进下界 | 至少有 f(n) 这么慢 |
Θ(f(n)) |
紧确界 | 上下界同阶 |
o(f(n)) |
严格低阶 | 相比 f(n) 可忽略 |
常见增长阶:
1 | O(1) < O(log n) < O(n) < O(n log n) < O(n^2) < O(n^3) < O(2^n) < O(n!) |
复盘时不要只背顺序,要能解释:
- 二分查找每次折半,所以是
O(log n)。 - 双重循环若每层都和
n同阶,通常是O(n^2)。 - 归并排序每层处理
n个元素,共log n层,所以是O(n log n)。 - 枚举所有排列通常到
O(n!)。
5、级数与循环估计
常见求和:
| 形式 | 阶 |
|---|---|
1 + 1 + ... + 1 |
O(n) |
1 + 2 + ... + n |
O(n^2) |
1 + 2 + 4 + ... + n |
O(n) |
n + n/2 + n/4 + ... |
O(n) |
1 + 1/2 + ... + 1/n |
O(log n) |
log 1 + log 2 + ... + log n |
O(n log n) |
循环估计流程:
1 | for (int i = 1; i <= n; i *= 2) { |
1 | for (int i = 0; i < n; ++i) { |
6、迭代、递归与递推式
递归分析要抓住两个东西:每层做多少事,问题规模如何缩小。
| 递推式 | 典型算法 | 复杂度 |
|---|---|---|
T(n)=T(n-1)+O(1) |
线性递归 | O(n) |
T(n)=T(n/2)+O(1) |
二分查找 | O(log n) |
T(n)=2T(n/2)+O(n) |
归并排序 | O(n log n) |
T(n)=T(n-1)+T(n-2)+O(1) |
朴素 Fibonacci | 指数级 |
朴素 Fibonacci 的问题是重复求解子问题:
1 | int fib(int n) { |
改成迭代或记忆化后,可以把复杂度降到 O(n):
1 | int fib(int n) { |
7、动态规划入口
动态规划适用于两类特征明显的问题:
| 特征 | 含义 |
|---|---|
| 最优子结构 | 大问题的最优解能由小问题最优解组合出来 |
| 重叠子问题 | 递归过程中反复求解同一批子问题 |
基本步骤:
- 定义状态。
- 写出状态转移。
- 确定初始状态。
- 确定计算顺序。
- 还原答案或路径。
数据结构课里,动态规划不是主线,但它帮助理解“算法设计不是只换结构,还要换求解方式”。
8、算法下界
下界讨论的是:不是某个算法不够聪明,而是问题本身至少要付出多少代价。
比较排序的经典结论:
1 | 任何基于比较的排序算法,最坏情况下需要 Ω(n log n) 次比较。 |
理由可以用决策树理解:
n个互异元素有n!种可能排列。- 每次比较最多把可能性分成两类。
- 决策树至少要有
n!个叶子。 - 高度至少是
log2(n!) = Ω(n log n)。
所以归并排序、堆排序的 O(n log n) 在比较排序模型下已经达到最优阶。
9、易错点
| 易错点 | 修正 |
|---|---|
| 把常数写进大 O | 渐进分析忽略常数和低阶项 |
| 只看循环层数 | 内层循环次数可能随外层变化 |
| 忽略输入规模 | 图算法常同时有 n 和 e |
| 混淆最坏和平均 | 散列表、快速排序尤其要分清 |
| 只会背结论 | 要能从递推式或操作次数推出复杂度 |
10、复盘清单
| 检查项 | 状态 |
|---|---|
| 能解释 RAM 模型和输入规模 | 待复盘 |
能区分 O、Ω、Θ |
待复盘 |
| 能估计常见循环复杂度 | 待复盘 |
| 能写出二分、归并、Fibonacci 的递推式 | 待复盘 |
| 能说明朴素递归为什么会重复计算 | 待复盘 |
| 能复述比较排序下界的决策树证明 | 待复盘 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ruiqy~!







