在软件开发与算法设计中,性能优化始终是我们绕不开的话题。衡量一个算法是否高效,主要依赖两个重要指标:时间复杂度(Time Complexity) 和 空间复杂度(Space Complexity)。
这篇文章将全面介绍这两个概念,辅以丰富的代码示例与对比,帮助你更深入地理解它们在实际开发中的应用。
🌟 一、时间复杂度(Time Complexity)
1. 什么是时间复杂度?
时间复杂度表示一个算法在运行过程中所执行的基本操作数(或步骤数),是输入规模 nn 的函数。我们通常用 大 O 符号(Big-O) 表示其增长率,比如:O(1)
、O(n)
、O(n²)
等。
2. 常见时间复杂度汇总
时间复杂度 | 描述 | 场景示例 |
---|---|---|
O(1) | 常数时间 | 访问数组、栈顶操作 |
O(log n) | 对数时间 | 二分查找、堆操作 |
O(n) | 线性时间 | 遍历数组、链表 |
O(n log n) | 线性对数时间 | 快速排序、归并排序 |
O(n²) | 平方时间 | 双重循环、冒泡排序 |
O(2ⁿ) | 指数时间 | 斐波那契递归、子集生成 |
O(n!) | 阶乘时间 | 全排列、TSP问题 |
3. 时间复杂度增长对比(当 n = 10 时)
输入规模 nn | O(1) | O(log n) | O(n) | O(n log n) | O(n²) | O(2ⁿ) | O(n!) |
---|---|---|---|---|---|---|---|
10 | 1 | ~3 | 10 | ~33 | 100 | 1024 | 3628800 |
✅ 说明:随着输入规模变大,高阶复杂度会呈现指数级增长,性能急剧下降,务必注意选择合适算法。
4. 时间复杂度示例代码详解
✅ O(1) — 常数时间复杂度
操作步骤固定,不随输入规模变化:
int getFirst(int[] arr) {
return arr[0]; // 始终只执行一次
}
✅ O(log n) — 对数时间复杂度
每次操作将问题规模减半:
int binarySearch(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
✅ O(n) — 线性时间复杂度
一次完整遍历所有元素:
void printAll(int[] arr) {
for (int num : arr) {
System.out.println(num);
}
}
✅ O(n log n) — 线性对数时间复杂度
归并排序是典型的例子:
void mergeSort(int[] arr, int l, int r) {
if (l >= r) return;
int mid = (l + r) / 2;
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
✅ O(n²) — 平方时间复杂度
嵌套循环处理两两组合:
void printPairs(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
System.out.println(arr[i] + ", " + arr[j]);
}
}
}
✅ O(2ⁿ) — 指数时间复杂度
斐波那契递归(未优化):
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
✅ O(n!) — 阶乘时间复杂度
全排列问题的回溯搜索:
void permute(List<Integer> list, int l, int r) {
if (l == r) {
System.out.println(list);
return;
}
for (int i = l; i <= r; i++) {
Collections.swap(list, l, i);
permute(list, l + 1, r);
Collections.swap(list, l, i);
}
}
💾 二、空间复杂度(Space Complexity)
1. 什么是空间复杂度?
空间复杂度是指算法在运行过程中额外占用的辅助存储空间,也是衡量算法效率的重要标准。
2. 常见空间复杂度汇总
空间复杂度 | 描述 | 场景示例 |
---|---|---|
O(1) | 常数空间 | 只使用固定数量变量 |
O(n) | 线性空间 | 数组复制、递归栈 |
O(n²) | 二维结构存储 | 图、DP表格、邻接矩阵 |
3. 空间复杂度示例代码详解
✅ O(1) — 常数空间复杂度
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
✅ O(n) — 线性空间复杂度
int[] copyArray(int[] arr) {
int[] newArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
return newArr;
}
✅ O(n) — 递归栈空间
int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
✅ O(n²) — 二维结构空间
int[][] matrix = new int[n][n]; // 占用 O(n²) 空间
🔁 三、时间与空间的权衡
✅ 空间换时间(记忆化递归)
Map<Integer, Integer> memo = new HashMap<>();
int fib(int n) {
if (n <= 1) return n;
if (memo.containsKey(n)) return memo.get(n);
int result = fib(n - 1) + fib(n - 2);
memo.put(n, result);
return result;
}
-
时间复杂度:O(n)
-
空间复杂度:O(n)
✅ 时间换空间(节省内存)
int fib(int n) {
if (n <= 1) return n;
int a = 0, b = 1;
for (int i = 2; i <= n; i++) {
int c = a + b;
a = b;
b = c;
}
return b;
}
-
时间复杂度:O(n)
-
空间复杂度:O(1)
📌 四、真实场景中的复杂度选择建议
应用场景 | 推荐算法 | 时间复杂度 |
---|---|---|
快速查找 | HashMap | O(1) |
有序数据查找 | 二分查找 | O(log n) |
数据排序 | 快排 / 归并排序 | O(n log n) |
图最短路径 | Dijkstra | O(E log V) |
全排列 | 回溯 / DFS | O(n!) |
✅ 五、总结
-
时间复杂度:衡量算法的执行效率。
-
空间复杂度:衡量算法的内存开销。
-
在实际开发中,性能优化要在“运行速度”和“资源消耗”之间取得平衡。
-
掌握复杂度分析能力,是面试与工作中不可或缺的基本功。