从代码出发理解时间复杂度与空间复杂度

从代码出发理解时间复杂度与空间复杂度

算法分析不追求算出程序运行多少毫秒,而是判断:当输入规模 N 变大时,运行时间和额外内存以什么速度增长。时间复杂度看基本操作的执行次数,空间复杂度看算法额外占用的存储空间。

一、为什么不能只看运行时间

同一段程序在不同电脑、编译器和输入数据上耗时不同。用输入规模 N 的函数来描述增长趋势,比某次运行的秒数更稳定,也更适合比较算法。

递归求斐波那契数是常见例子。它会反复计算相同的子问题,N 小时看不出代价,N 一大,调用次数就迅速膨胀------这正是复杂度分析要提前揭示的风险。

二、用大 O 表示法抓住主要增长项

大 O 表示法只保留决定增长速度的部分:

  1. 常数统一记为 1;
  2. 只保留最高阶项;
  3. 去掉最高阶项前的常数系数。

若一个函数的基本操作次数为 N² + 2N + 10,当 N 足够大时,N² 决定整体增长趋势,因此时间复杂度记为 O(N²)。

三、从代码判断时间复杂度

1. 双层循环:O(N²)

c 复制代码
for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j) {
        // 基本操作
    }
}

外层循环执行 N 次,每次又执行 N 次内层循环,核心操作约执行 N×N 次,所以是 O(N²)。循环前后的少量常数操作不改变结论。

2. 单层循环:O(N)

代码只从头到尾遍历一次长度为 N 的数据,核心操作次数与 N 成正比,时间复杂度就是 O(N)。若分别遍历长度为 N 和 M 的两组数据,应写成 O(N+M),不能在没有关系的情况下把 M 偷换成 N。

3. 固定次数循环:O(1)

c 复制代码
for (int i = 0; i < 100; ++i) {
    // 基本操作
}

循环次数固定为 100,不随输入规模变化,因此记为 O(1)。O(1) 并不表示只执行一次,而是执行次数是常数。

4. 二分查找:O(log N)

二分查找每比较一次,就把待查区间缩小一半。经过 k 次后,区间规模约为 N/2ᵏ;当区间缩小到 1 时,k≈log₂N,因此复杂度为 O(log N)。

5. 冒泡排序:最坏 O(N²)

冒泡排序要进行多轮相邻元素比较。最坏情况下,比较次数的数量级为 N²,因此通常记为 O(N²)。

6. 递归:既看调用次数,也看调用深度

递归阶乘会沿着 N、N-1、N-2......一直调用到终止条件,调用次数与 N 成正比,时间复杂度为 O(N)。朴素递归斐波那契会分裂出大量重复调用,时间复杂度可达 O(2ᴺ)。

四、最好、平均与最坏情况

同一算法可能因为输入不同而执行不同次数。以顺序查找为例:

  • 第一个元素就命中:最好 O(1);
  • 目标在中间:平均仍是 O(N);
  • 目标在末尾或不存在:最坏 O(N)。

工程分析通常优先关注最坏情况,因为它给出了资源消耗的上界。

五、空间复杂度怎么看

空间复杂度衡量的是额外空间随 N 的增长趋势,不是程序本身占用的全部内存。

  • 只使用少量局部变量:O(1);
  • 动态申请 N 个元素的数组:O(N);
  • 递归调用深度为 N:调用栈需要 O(N) 空间。

递归函数即使每一层只定义一个常量级变量,所有尚未返回的栈帧叠加后,仍可能产生线性空间开销。

六、常见复杂度的增长顺序

从增长较慢到较快:

O(1) < O(log N) < O(N) < O(N log N) < O(N²) < O(2ᴺ) < O(N!)

N 很大时,复杂度级别的差异会远远超过常数优化带来的收益。先选对算法级别,再讨论局部优化,通常更有效。

七、实用分析清单

拿到一段代码时,可以依次检查:

  1. 输入规模 N 代表什么;
  2. 最频繁的基本操作是什么;
  3. 循环是否嵌套,边界是否依赖 N;
  4. 每次迭代是否把问题规模减半;
  5. 递归产生多少次调用,最大调用深度是多少;
  6. 是否申请了随 N 增长的数组、链表或其他辅助空间;
  7. 结论描述的是最好、平均还是最坏情况。

复杂度分析的核心不是背公式,而是建立"代码结构---执行次数---增长趋势"之间的对应关系。

相关推荐
lzhdim4 小时前
AI的分类
人工智能·算法·机器学习·分类·数据挖掘
凤凰院凶涛QAQ7 小时前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
H.莓飛7 小时前
【数据结构】排序_OJ题(一):计数、桶与排序后的处理
java·开发语言·数据结构
w_zero_one9 小时前
算法--模拟
java·数据结构·算法
秋天的怀念_David9 小时前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
秋天的怀念_David10 小时前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
薛定e的猫咪10 小时前
(ICLR 2020)MAML 靠的不是快速学习,而是特征复用
人工智能·深度学习·学习·算法·机器学习
薛定e的猫咪10 小时前
(ICLR 2026)DF-META:面向 FJSP 的无导数元学习实例自适应调度
人工智能·深度学习·学习·算法·机器学习
小羊没烦恼!12 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
wanderist.12 小时前
202609CSP认证P3
算法