算法分析
含AI生成内容
算法分析
算法的基本概念
算法(Algorithm)是对特定问题求解步骤的描述,是一个有穷的指令序列。算法与程序不同——程序可以无穷运行(如操作系统主循环),但算法必须有穷。
算法的五大特性
| 特性 | 含义 | 说明 |
|---|---|---|
| 有穷性 | 必须在有限步后结束,每一步在有限时间内完成 | 算法必须有终止条件;程序未必有穷 |
| 确定性 | 每条指令含义明确,相同输入只能产生相同输出 | 不能有二义性 |
| 可行性 | 所有操作都可通过已实现的基本运算有限次执行完成 | 理论上的操作必须能实际执行 |
| 输入 | 有零个或多个输入 | 取自特定数据对象的集合 |
| 输出 | 至少有一个输出 | 结果是算法存在的意义 |
易错:有穷性强调”会停下来”,确定性强调”没有歧义”,两者是不同的概念。408 选择题经常将这两个特性放在一起让考生辨析。
好算法的四个目标
| 目标 | 含义 |
|---|---|
| 正确性 | 算法能正确解决问题,满足需求规格 |
| 可读性 | 便于理解、调试和维护(不追求极致技巧而牺牲可读性) |
| 健壮性 | 能处理非法输入,不会异常崩溃 |
| 高效率与低存储 | 时间复杂度和空间复杂度尽可能低 |
时间复杂度
时间复杂度是对算法运行时间随问题规模增长而增长的趋势的度量。实际使用事前分析法——统计算法中基本操作的执行次数 T(n) 作为时间度量,而非具体的运行时间(运行时间受硬件、编程语言等外部因素影响)。
渐近记号
| 记号 | 名称 | 数学含义 | 直观理解 |
|---|---|---|---|
| O (Big O) | 渐近上界 | T(n) ≤ C·f(n) 当 n ≥ n₀ | 算法不会比 f(n) 更差 |
| Ω (Omega) | 渐近下界 | T(n) ≥ C·g(n) 当 n ≥ n₀ | 算法不会比 g(n) 更好 |
| Θ (Theta) | 渐近紧界 | C₁·h(n) ≤ T(n) ≤ C₂·h(n) 当 n ≥ n₀ | 算法以 h(n) 为增长量级 |
- 大 O 记号是 408 考研中使用最频繁的记号。默认情况下,题目问”时间复杂度”通常指最坏时间复杂度,并用大 O 表示。
- 大 O 记号忽略低阶项和常数系数,只保留最高阶项。
计算规则:
- 加法规则:T(n) = T₁(n) + T₂(n) = O(f(n)) + O(g(n)) = O(max(f(n), g(n)))
- 乘法规则:T(n) = T₁(n) × T₂(n) = O(f(n)) × O(g(n)) = O(f(n) × g(n))
常见时间复杂度排序
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)| 复杂度 | 名称 | 典型算法 | 说明 |
|---|---|---|---|
| O(1) | 常数阶 | 哈希查找、数组按下标访问 | 执行时间与 n 无关 |
| O(log n) | 对数阶 | 折半查找、BST 查找(平均) | 每次迭代将问题规模减半 |
| O(n) | 线性阶 | 顺序查找、遍历 | 执行时间与 n 成正比 |
| O(n log n) | 线性对数阶 | 快速排序、归并排序、堆排序 | 分治策略的典型复杂度 |
| O(n²) | 平方阶 | 冒泡排序、直接插入排序、简单选择排序 | 双重循环 |
| O(n³) | 立方阶 | 矩阵乘法的朴素算法 | 三重循环 |
| O(2ⁿ) | 指数阶 | 穷举子集 | n 稍大即不可行 |
| O(n!) | 阶乘阶 | 穷举全排列 | 增长速度极快 |
典型分析模式
// 模式 1:单层循环 → O(n)for (int i = 0; i < n; i++) x++;
// 模式 2:双层嵌套循环 → O(n²)for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) x++;
// 模式 3:对数循环 → O(log n)int i = 1;while (i <= n) i = i * 2; // 执行次数 = ⌈log₂n⌉
// 模式 4:循环变量倍增 → O(√n)int i = 0;while (i * i <= n) i++;
// 模式 5:嵌套但内层与外层相关 → O(n²)for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) // 总次数 = 0+1+2+...+(n-1) = n(n-1)/2 x++;易错:判断循环复杂度时,不要只看有几层循环,还要看循环变量的变化方式。对数是 O(log n) 而不是 O(n),倍增/减半(i*=2 / i/=2)是对数增长的典型特征。
最好、最坏与平均时间复杂度
| 情况 | 含义 | 考研中的用法 |
|---|---|---|
| 最好时间复杂度 | 输入最有利时的运行时间 | 参考价值有限 |
| 最坏时间复杂度 | 输入最不利时的运行时间 | 考试默认就是最坏情况 |
| 平均时间复杂度 | 所有可能输入的加权平均 | 需要明确说明时才计算 |
递归算法的时间复杂度
递归算法的时间复杂度分析通常通过递推公式进行。常见形式:
- T(n) = T(n-1) + O(1) → O(n)(如递归遍历链表)
- T(n) = 2T(n/2) + O(n) → O(n log n)(如归并排序)
- T(n) = T(n/2) + O(1) → O(log n)(如折半查找的递归版)
- T(n) = 2T(n-1) + O(1) → O(2ⁿ)(如斐波那契数列的朴素递归)
主定理(Master Theorem):对于形如 T(n) = aT(n/b) + f(n) 的递推式,主定理提供了快速求解渐近紧界的框架。408 考研中可以直接用递推展开法求解,主定理可作为补充。
空间复杂度
空间复杂度 S(n) 度量的是算法除输入数据外额外需要的辅助空间大小。
常见空间复杂度
| 空间复杂度 | 含义 | 示例 | |
|---|---|---|---|
| O(1) | 原地工作,只用常数个额外变量 | 冒泡排序、直接插入排序、链表逆置 | |
| O(n) | 需要与输入规模同量级的辅助空间 | 归并排序的辅助数组、顺序表动态扩容 | |
| O(log n) | 递归深度为 log n | 快速排序的递归栈(平均情况) | |
| O(n) | 递归深度为 n | 快速排序的递归栈(最坏情况) |
注意:递归算法的空间复杂度 = 递归调用的深度(每层递归在栈上占用常数空间时)。
传值与传指针的空间开销
- 传值(Pass by Value):将实参的完整副本传递给函数。如果实参是大型结构体(如包含大量元素的数组),传值会造成 O(n) 的额外空间开销。
- 传指针/引用(Pass by Pointer/Reference):只传递地址(4 或 8 字节),无论实参多大,空间开销始终为 O(1)。408 伪代码中的
&符号(C++ 引用)即表示传引用。
易错:分析空间复杂度时,函数的形参也需要考虑。传入的数组指针本身(4 或 8 字节)不算额外空间,但如果函数通过传值方式接收整个数组,则需要将数组大小计入空间复杂度。
复杂度分析实例
复杂度分析方法在实际数据结构中的应用:
- 顺序表(Sequential List):按位查找 O(1)、按值查找 O(n)、插入删除 O(n)(平均移动 n/2 个元素)——这些复杂度分析直接决定顺序表的适用场景
- 链表(Linked List):按位查找 O(n)(顺序访问)、已知前驱时插入 O(1)——链表的 O(n) 查找成本是其核心局限
考研高频考点
- ⭐ 给定代码段,分析时间复杂度(选择题/应用题几乎每年必考)
- ⭐ 常见复杂度的大小排序(选择题)
- ⭐ 递归算法的时间/空间复杂度分析(难度较高,应用题偶考)
- ⭐ 算法的五个特性(选择题)
- 大 O 记号的数学定义
- 最好/最坏/平均时间复杂度的区别
- 空间复杂度的含义(不包含输入数据占用的空间)
- 传值与传指针在空间开销上的区别
易错:时间复杂度分析的是基本操作的执行次数,不是具体运行时间。O(n) 的算法不一定比 O(n²) 的算法快——大 O 描述的是增长趋势,只在 n 足够大时才有意义。
易错:算法不一定要写成程序——可以用自然语言、流程图、伪代码等描述。有穷性是算法与程序的本质区别。
关联页面
- 顺序表(Sequential List)— 复杂度分析在顺序表中的应用实例
- 链表(Linked List) — 复杂度分析在链表中的应用实例
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!










