📄 一句话概括
大 O 表示法回答一个问题:【数据翻十倍,程序慢多少?】——它扔掉常数与低阶项,只看量级曲线:O(1) 不慌、O(log n) 从容、O(n) 跟着涨、O(n²) 会爆炸——算法选型的一切账都从这张表算起。
📝 本节和前面几节的关系
4-11~4-13 一直在说【O(n²)】【O(n log n)】,这一篇把这两个记号说透。回收 4-13 的归并账(n x log n 的来历)与 4-15 的二分(log n 的代表)。
一、大 O 只看【增长曲线】
定义的白话版:数据规模 n 变大时,操作次数大约跟着哪条曲线涨。三个忽略:
- 忽略常数:3n 次和 n 次都记 O(n)——常数靠硬件与实现,量级才定生死;
- 忽略低阶项:n² + 100n + 500 在 n 大时 n² 独大——记 O(n²);
- 只看最坏趋势:大 O 通常按最坏情况写保险。
为什么可以这么粗暴:n 小的时候【什么都快】,n 大的时候量级差是天文数字——
| 规模 | O(log n)(二分) | O(n)(线性) | O(n log n)(快排) | O(n²)(冒泡) |
|---|---|---|---|---|
| 10 | 4 | 10 | 33 | 100 |
| 1 000 | 10 | 1 000 | 1 万 | 100 万 |
| 100 万 | 20 | 100 万 | 2 000 万 | 1 万亿 |
一百万数据把 O(n²) 和 O(n log n) 拉开约五万倍——这就是【算法比硬件值钱】的账。
二、常见档位一张表(按快到慢)
| 大 O | 名字 | 代表 | n 翻 10 倍时 |
|---|---|---|---|
| O(1) | 常数 | 字典取值(3-13)、下标访问 | 不变 |
| O(log n) | 对数 | 二分查找(4-15)、平衡树(3-17) | 多约 3 次操作 |
| O(n) | 线性 | 线性查找、遍历一遍 | 慢 10 倍 |
| O(n log n) | 线性对数 | 归并、快排、Timsort | 慢约 10 倍多一点 |
| O(n²) | 平方 | 冒泡、选择、插入 | 慢 100 倍 |
| O(2ⁿ) | 指数 | 暴力穷举所有子集 | 慢 1000 倍 |
读法:n 翻 10 倍、O(n²) 慢 100 倍、O(2ⁿ) 慢一千多倍——平方往下是工程红线,指数是数据超过 30 就跑不动。
三、复杂度怎么数:看循环嵌套
- 单层循环跑 n 次 → O(n);
- 两层嵌套、每层 n → O(n²)(4-11 的 n(n-1)/2 里低阶项被扔掉);
- 每次把范围砍半(二分)→ O(log n);
- 每层合并 O(n)、共 log n 层(归并)→ O(n log n)。
空间复杂度同款记号量【内存】:归并 O(n)(辅助数组)、快排 O(log n)(递归栈)、原地交换 O(1)。
四、动手 Demo:两档算法的肉眼差距
import time, random
data = [random.randint(0, 9) for _ in range(20000)]
target_sorted = sorted(data) # Timsort:O(n log n),Python 内置
def bubble(arr): # O(n²)
a = arr.copy()
for i in range(len(a) - 1):
for j in range(len(a) - 1 - i):
if a[j] > a[j + 1]:
a[j], a[j + 1] = a[j + 1], a[j]
return a
t0 = time.perf_counter(); bubble(data); t1 = time.perf_counter()
t2 = time.perf_counter(); sorted(data); t3 = time.perf_counter()
print("冒泡:", round(t1 - t0, 3), "秒")
print("Timsort:", round(t3 - t2, 3), "秒")
看到什么算成功(参考量级,机器不同数值不同):冒泡约 5~15 秒,Timsort 约 0.005 秒——两三千倍的差距,2 万条数据就现形。
读结果:同一份数据、同一台机器,唯一差别是量级(O(n²) vs O(n log n))。把数据翻到 20 万条,冒泡要按【小时】算、Timsort 仍零点零几秒——曲线不骗人。
五、30 秒实验
跑 import math; print(math.log2(1_000_000))——约 19.93。一百万数据二分只要 20 次——这句背过的话今天用 log2 验了一遍(回收 3-17 的【二十次锁定】)。
六、常见误区
- 【大 O 精确等于运行时间】——错。它只讲增长量级:O(n) 的算法常数可能是 O(n²) 的一万倍,n 小的时候后者反而快——量级管大局,常数管小数据。
- 【优化常数能救 O(n²)】——救不了。常数优化是【快两三倍】;量级差是【差一万倍】——数据一大必须换算法,调参救不了曲线。
- 【只看时间不看空间】——片面。归并 O(n) 辅助内存、动态规划动辄 O(n²) 空间——时间空间是一张表的两列,一起看。
自测三问(点开对答案)
1. **大 O 为什么可以忽略常数和低阶项?** 答:n 大时量级项独大——常数靠硬件改善,低阶项可忽略;量级决定【数据翻倍、耗时怎么涨】这个大局。 2. **一百万数据二分查找大约几次?对应哪个档位?** 答:约 20 次(log2 百万 ≈ 19.9)——O(log n) 档。 3. **为什么说 O(2ⁿ) 实战上等于不可用?** 答:n 翻 10 倍操作涨一千多倍、n 每加 1 翻一倍——数据到三四十个就已经按年计,只配写进【反面教材】。- ☐ 下一节(4-15 线性查找、二分查找)
相关笔记
- 归并与快排 —— O(n log n) 的来历
- 关联数组、哈希函数 —— O(1) 的代表选手
- 线性与二分查找 —— O(n) 对 O(log n) 正面交锋
- ⌨️ 编程原理 · 索引 —— 全课路线图