欢迎光临
我们一直在努力

5分钟彻底搞懂时间复杂度与空间复杂度(C语言版)

复杂度分析就像“给算法做体检”——不用跑代码,就能提前判断程序能不能扛住大数据。
本文用最生活化的例子 + C 语言代码,帮你一口气掌握大O、时间复杂度、空间复杂度。

一、为什么需要复杂度分析?

假如你写了一个排序,100 个数据很快,但 100 万个数据直接卡死——问题就出在算法效率上。
大 O 表示法 关注的是数据规模 n 变得非常大时,执行次数或空间占用的增长趋势,而不是具体秒数。


二、时间复杂度 (Time Complexity)

1. 什么是大 O ?

大 O 只统计核心代码执行次数的数量级。
规则:忽略常数、系数和低阶项,只保留最高阶。
例如执行次数 3n² + 2n + 100,时间复杂度就是 O(n²)。

2. 快速推导方法

找出代码中执行次数最多的语句,看它随着 n 如何增长。


3. 常见时间复杂度(C 语言代码示例)

复杂度名称常见场景直观感受
O(1) 常数阶 取数组第一个元素 秒杀
O(log n) 对数阶 二分查找 超快
O(n) 线性阶 遍历数组 还行
O(n log n) 线性对数阶 归并/快速排序 稍慢但可接受
O(n²) 平方阶 冒泡/插入排序 数据大就崩溃
O(2ⁿ) 指数阶 递归求斐波那契 噩梦

🔹 O(1) – 常数阶

int get_first(int arr[]) {
return arr[0]; // 不管数组多大,只执行一次
}
就像从书架拿第一本书,和书的总量无关。函数只接收数组指针,完全不关心数组大小。

#### 🔹 O(log n) – 对数阶(二分查找)
```c
int binary_search(int arr[], int n, int target) {
int left = 0, right = n 1;
while (left <= right) {
int mid = left + (right left) / 2;
if (arr[mid] == target)
return mid;
else if (arr[mid] < target)
left = mid + 1;
else
right = mid 1;
}
return 1;
}

每次砍掉一半数据,数据量翻倍只会多执行一次。查字典就是二分思想。

🔹 O(n) – 线性阶

void print_all(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]); // 有 n 个元素就执行 n 次
}
}

像抄写名单,人越多花的时间越长。

🔹 O(n log n) – 线性对数阶

归并排序、快速排序的典型复杂度,可以理解为:每层处理 n 个元素,一共有 log n 层,总操作量 n × log n。

🔹 O(n²) – 平方阶(冒泡排序)

void bubble_sort(int arr[], int n) {
for (int i = 0; i < n 1; i++) {
for (int j = 0; j < n i 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

双重嵌套循环,数据量增大 10 倍,执行次数增大 100 倍。

🔹 O(2ⁿ) – 指数阶(递归斐波那契)

int fib(int n) {
if (n <= 1)
return n;
return fib(n 1) + fib(n 2);
}

这种递归像细胞分裂,n=40 时程序就基本跑不动了,一定要避免。


4. 计算技巧速记

  • 单层循环 → O(n)
  • 两层嵌套循环 → O(n²)
  • 循环中每次规模减半 → O(log n)
  • 递归调用两次及以上且不记忆 → 常是指数级
  • 只保留最大量级,去掉常数和系数

三、空间复杂度 (Space Complexity)

空间复杂度衡量的是临时额外占用的存储空间,同样用大 O 表示。
关注:变量、数组、递归栈等,输入数据本身不算。

空间复杂度实例说明
O(1) 变量交换、原地排序 极省内存
O(n) 复制数组、递归深度 n 额外开辟线性空间
O(n²) 创建 n×n 矩阵 例如动态规划表格

🔸 O(1) 空间

int sum_arr(int arr[], int n) {
int total = 0; // 只有一个临时变量
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}

无论数据多大,额外空间恒定。

🔸 O(n) 空间(动态开辟数组)

#include <stdlib.h>
int* copy_arr(int arr[], int n) {
int* new_arr = (int*)malloc(n * sizeof(int)); // 开辟 n 个 int 空间
for (int i = 0; i < n; i++) {
new_arr[i] = arr[i];
}
return new_arr; // 调用者记得 free
}

临时数组大小随着 n 线性增长。

🔸 递归中的空间陷阱

int fact(int n) {
if (n == 1) return 1;
return n * fact(n 1); // 每次调用都会压栈,深度为 n
}

递归栈的深度为 n,空间复杂度就是 O(n)。
(如果递归深度过大可能造成栈溢出,这也是空间复杂度的现实意义。)


四、时间 vs 空间:如何取舍?

“鱼和熊掌”的现实:

  • 追求更快 → 可能要用缓存、多存中间结果 → 空间消耗上升
  • 内存紧张 → 偏向原地操作、少开辟新数组 → 时间可能变长

经典例子“两数之和”:

  • 暴力双层循环 → O(n²) 时间,O(1) 空间
  • 哈希表查找 → O(n) 时间,O(n) 空间

实际开发中根据数据量和硬件条件灵活平衡。


五、一张图总结

时间复杂度空间复杂度
定义 执行次数随 n 增长的趋势 临时空间随 n 增长的趋势
常用单位 O(1), O(log n), O(n), O(n²)… O(1), O(n), O(n²)…
如何看 找执行次数最多的代码 看额外开辟的数组/递归栈深度

掌握这两个复杂度,刷题、面试、写项目都能游刃有余。
希望这篇 C 语言版讲解能帮你彻底打通“任督二脉”!


如果觉得有帮助,欢迎点赞收藏,让更多伙伴看到~

赞(0)
未经允许不得转载:171主机测评 » 5分钟彻底搞懂时间复杂度与空间复杂度(C语言版)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址