绪论与复杂度分析

考点定位

  • 所属大纲章节:绪论(时间复杂度与空间复杂度分析)
  • 常见题型:选择题(给定代码片段判断复杂度、给定递推式判断复杂度量级、复杂度大小比较)
  • 考查频率:高。复杂度分析本身几乎每年以 1-2 道选择题出现,且是所有算法设计大题的必答项(最后一问"分析算法时间复杂度"白送分或白丢分)

工程直觉迁移:你每天都在做复杂度判断,这部分真正的增量只有两块——渐进符号的数学定义(选择题会抠字眼)和递推式的系统解法(主方法)。

基本概念:数据结构、算法与复杂度的严格定义

选择题会直接考分类,定义先行:

概念严格定义
数据结构相互之间存在一种或多种特定关系的数据元素的集合,研究逻辑结构(元素间关系)与存储结构(逻辑结构在存储器中的映射,又称物理结构)
逻辑结构四大类集合(仅同属一个集合)、线性结构(一对一)、树形结构(一对多)、图状/网状结构(多对多)
存储结构四种顺序存储、链式存储、索引存储、散列(哈希)存储
算法对特定问题求解步骤的一种描述,是指令的有限序列;五个特性:有穷性、确定性、可行性、输入(0 个或多个)、输出(1 个或多个)
时间复杂度 T(n)T(n)算法中基本运算(最深层循环内的语句)的执行次数关于问题规模 nn 的函数的渐进量度
空间复杂度 S(n)S(n)算法运行所需辅助存储空间关于 nn 的量度(原地算法为 O(1)O(1))

两条选择题级推论:逻辑结构独立于存储结构——同一线性结构可用顺序或链式两种存储实现(见 02-线性表);算法与程序的区别在有穷性——操作系统是无限循环的程序,不是算法。

渐进符号的数学表述

选择题最爱考定义的精确性,尤其是量词方向。设 f(n)f(n)、g(n)g(n) 定义在正整数上:

O(f)={g(n)∣∃ c>0, ∃ n0, ∀ n≥n0: 0≤g(n)≤c⋅f(n)}O(f) = \{ g(n) \mid \exists\, c > 0,\ \exists\, n_0,\ \forall\, n \ge n_0:\ 0 \le g(n) \le c \cdot f(n) \}

OO 是上界(渐进小于等于),Ω\Omega 是下界,Θ\Theta 是紧确界(同时满足两者):

Ω(f)={g(n)∣∃ c>0, ∃ n0, ∀ n≥n0: g(n)≥c⋅f(n)≥0}\Omega(f) = \{ g(n) \mid \exists\, c > 0,\ \exists\, n_0,\ \forall\, n \ge n_0:\ g(n) \ge c \cdot f(n) \ge 0 \}

Θ(f)={g(n)∣∃ c1,c2>0, ∃ n0, ∀ n≥n0: c1f(n)≤g(n)≤c2f(n)}\Theta(f) = \{ g(n) \mid \exists\, c_1, c_2 > 0,\ \exists\, n_0,\ \forall\, n \ge n_0:\ c_1 f(n) \le g(n) \le c_2 f(n) \}

考试抠字眼的方式:

说法对错说明
f(n)=O(g(n))f(n) = O(g(n)) 中 O(g(n))O(g(n)) 表示一个函数集合,"=="实际是"∈\in"对严格写法是 f(n)∈O(g(n))f(n) \in O(g(n))
O(f)O(f) 与 o(f)o(f) 的区别:oo 要求 lim⁡n→∞f(n)g(n)=0\lim_{n\to\infty} \frac{f(n)}{g(n)} = 0 类型的严格小于考纲内一般只考 O/Ω/ΘO/\Omega/\Theta,知道 oo 是"严格上界、不含等号"即可低频
f(n)=O(n2)f(n) = O(n^2) 则 f(n)=O(n3)f(n) = O(n^3) 也成立对上界不唯一,选择题常拿这点设陷阱
常见量级链:log⁡∗n<log⁡log⁡n<log⁡n<n<n<nlog⁡n<n2<n3<2n<n!\log^* n < \log\log n < \log n < \sqrt{n} < n < n\log n < n^2 < n^3 < 2^n < n!必背比较类选择题直接用

复杂度计算的选择题考法

考法一:循环计数

不要模拟执行,数最内层语句的执行次数关于 nn 的表达式:

int i = 1;
while (i <= n)
    i = i * 2;   // 执行次数 = floor(log2 n) + 1,复杂度 O(log n)
for (i = 1; i <= n; i++)        // i = 1..n
    for (j = 1; j <= i; j++)    // 内层共 1+2+...+n = n(n+1)/2
        x++;                    // O(n^2)

变式:内层是 for (j = 1; j < 2*i; j++)、双重循环一个乘 2 一个减 1、三层嵌套——统统归约为"内层次数求和"。

考法二:递推展开

形如"每次规模减半再做 O(1)O(1) 工作":T(n)=T(n/2)+c⇒T(n)=O(log⁡n)T(n) = T(n/2) + c \Rightarrow T(n) = O(\log n)(这就是二分查找,也是选择题常客)。

补:二分查找(折半查找)指在有序顺序表中,每轮将待查值与当前区间的中间元素比较,相等则成功,否则按大小关系舍弃一半区间、在另一半继续——每轮区间规模减半,全站后续章节引用"折半查找"均指此算法。

考法三:主方法(Master Method)

408 大纲未显式列主方法,但递归复杂度选择题用它秒杀。对

T(n)=a T(n/b)+f(n),a≥1, b>1T(n) = a\,T(n/b) + f(n), \quad a \ge 1,\ b > 1

记 nlog⁡ban^{\log_b a},与 f(n)f(n) 比较:

情形条件结论
1f(n)=O(nlog⁡ba−ϵ)f(n) = O(n^{\log_b a - \epsilon})T(n)=Θ(nlog⁡ba)T(n) = \Theta(n^{\log_b a})
2f(n)=Θ(nlog⁡ba)f(n) = \Theta(n^{\log_b a})(多项式意义下)T(n)=Θ(nlog⁡balog⁡n)T(n) = \Theta(n^{\log_b a} \log n)
3f(n)=Ω(nlog⁡ba+ϵ)f(n) = \Omega(n^{\log_b a + \epsilon}) 且正则条件T(n)=Θ(f(n))T(n) = \Theta(f(n))

速用实例:

  • 归并排序 T(n)=2T(n/2)+O(n)T(n) = 2T(n/2) + O(n):log⁡22=1\log_2 2 = 1,f(n)=nf(n)=n 与 n1n^1 同阶 → O(nlog⁡n)O(n\log n)
  • 二分查找 T(n)=T(n/2)+O(1)T(n) = T(n/2) + O(1):nlog⁡21=1n^{\log_2 1} = 1,f=1f=1 同阶 → O(log⁡n)O(\log n)
  • 快速排序最好情况 T(n)=2T(n/2)+O(n)T(n)=2T(n/2)+O(n) → O(nlog⁡n)O(n\log n);最坏 T(n)=T(n−1)+O(n)T(n)=T(n-1)+O(n) 不满足主方法形式(b=1b=1 不允许),直接展开为 ∑i=O(n2)\sum i = O(n^2)

坑:T(n)=2T(n/2)+nlog⁡nT(n) = 2T(n/2) + n\log n 落在情形 2 与 3 之间的缝隙,结论是 O(nlog⁡2n)O(n\log^2 n)——408 选择题基本不碰这种边角,遇到先检查是不是自己把递推式列错了。

例题(模拟题)

例 1:以下代码片段的时间复杂度为( )

int count = 0;
for (i = 1; i <= n; i *= 2)
    for (j = 1; j <= n; j++)
        count++;

A. O(n)O(n) B. O(nlog⁡n)O(n\log n) C. O(n2)O(n^2) D. O(log⁡n)O(\log n)

解答:外层执行 O(log⁡n)O(\log n) 次,每次内层 O(n)O(n),相乘得 O(nlog⁡n)O(n\log n),选 B。错因提醒:把外层次数看成 nn。

例 2:T(n)=4T(n/2)+nT(n) = 4T(n/2) + n 的渐进时间复杂度为( )

A. O(n)O(n) B. O(nlog⁡n)O(n\log n) C. O(n2)O(n^2) D. O(n2log⁡n)O(n^2\log n)

解答:nlog⁡24=n2n^{\log_2 4} = n^2,f(n)=n=O(n2−ϵ)f(n)=n = O(n^{2-\epsilon}),情形 1,T(n)=Θ(n2)T(n)=\Theta(n^2),选 C。

常见坑

  1. 加法与乘法规则:顺序执行取 max,嵌套执行取乘积。选择题会给出"并列两段循环,一段 O(n2)O(n^2) 一段 O(nlog⁡n)O(n\log n)"问总复杂度,答案是 O(n2)O(n^2)。
  2. log⁡\log 的底数无关性:log⁡an=log⁡bn/log⁡ba\log_a n = \log_b n / \log_b a,底数只差常数因子,复杂度里一律写 log⁡n\log n。
  3. 递归调用的空间复杂度:栈深度等于递归深度,不是调用总次数。n!n! 递归空间 O(n)O(n);二分查找递归空间 O(log⁡n)O(\log n)。
  4. 最好/最坏/平均:题目没说明时,默认问最坏时间复杂度;题干明确问"平均"时注意等概率假设(如顺序表插入平均移动 n/2n/2 次)。

自测

  1. 用 O/Ω/ΘO/\Omega/\Theta 定义说明:2n2+3nlog⁡n=Θ(n2)2n^2 + 3n\log n = \Theta(n^2) 是否成立?给出常数。
  2. for (i=0; i<n; i++) for (j=i; j<n; j++) x++; 的复杂度?
  3. T(n)=3T(n/4)+nlog⁡nT(n) = 3T(n/4) + n\log n 的复杂度量级?(提示:nlog⁡43≈n0.79n^{\log_4 3} \approx n^{0.79})
  4. 递归计算斐波那契数列 fib(n)(两次自调用)的时间复杂度是多少?为什么?

答案与简析

  1. 成立。取 c1=2c_1 = 2、c2=3c_2 = 3(当 nn 足够大时 2n2≤2n2+3nlog⁡n≤3n22n^2 \le 2n^2+3n\log n \le 3n^2)。
  2. 内层次数 =(n)+(n−1)+⋯+1=n(n+1)2= (n) + (n-1) + \cdots + 1 = \frac{n(n+1)}{2},O(n2)O(n^2)。
  3. f(n)=nlog⁡n=Ω(n0.79+ϵ)f(n) = n\log n = \Omega(n^{0.79+\epsilon}),情形 3,T(n)=O(nlog⁡n)T(n) = O(n\log n)。
  4. T(n)=T(n−1)+T(n−2)+O(1)T(n) = T(n-1)+T(n-2)+O(1),增长率为黄金分割比的幂,O(φn)≈O(1.618n)O(\varphi^n) \approx O(1.618^n),指数级。