根据左哥的思路,通俗易懂地理解算法复杂度。
1. 时间复杂度基础概念
把算法比作做饭:
O(1):相当于拿出一个速食面,直接泡
O(n):相当于炒一盘菜,食材越多,时间越长
O(n²):相当于给n个人每人炒n个菜
O(logn):相当于每次处理时食材减半(比如切西瓜)
2. 常见时间复杂度排序(从快到慢)
O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(2ⁿ)
具体例子:
数组长度n=100时:
O(1) = 1次操作
O(logn) ≈ 7次操作 (2⁷ ≈ 100)
O(n) = 100次操作
O(nlogn) ≈ 700次操作
O(n²) = 10000次操作
O(2ⁿ) = 天文数字
3. 如何计算时间复杂度
基本法则:
- 只看最高项
for(int i = 0; i < n; i++) {
System.out.println(i); // 执行n次
}
System.out.println("完成"); // 执行1次
// O(n + 1) => O(n)
- 忽略系数
for(int i = 0; i < 2n; i++) { // 执行2n次
System.out.println(i);
}
// O(2n) => O(n)
- 嵌套循环相乘
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
System.out.println(i + j);
}
}
// O(n * n) = O(n²)
4. 常见代码的复杂度分析
O(1) 常数时间
int a = 1;
int b = 2;
int c = a + b;
O(n) 线性时间
// 遍历一次数组
for(int i = 0; i < n; i++) {
System.out.println(arr[i]);
}
O(logn) 对数时间
// 二分查找
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;
}
O(nlogn) 线性对数时间
// 归并排序
public void mergeSort(int[] arr) {
// 每次分成两半 O(logn)
// 每层都要处理n个数 O(n)
// 总复杂度 O(nlogn)
}
5. 实际案例分析
例1:找数组最大值
int findMax(int[] arr) {
int max = arr[0];
for(int i = 1; i < arr.length; i++) {
if(arr[i] > max) max = arr[i];
}
return max;
}
// 复杂度:O(n)
例2:冒泡排序
void bubbleSort(int[] arr) {
for(int i = 0; i < arr.length - 1; i++) {
for(int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j+1]) {
// 交换
}
}
}
}
// 复杂度:O(n²)
6. 空间复杂度
空间复杂度看额外使用的空间:
// O(1) 空间
int sum(int[] arr) {
int result = 0; // 只用了一个变量
for(int num : arr) {
result += num;
}
return result;
}
// 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;
}
7. 实用技巧
-
看循环次数
- 一层循环:一般是O(n)
- 两层循环:一般是O(n²)
- 每次规模减半:一般是O(logn)
-
常见算法复杂度
- 二分查找:O(logn)
- 快速排序:O(nlogn)
- 冒泡排序:O(n²)
- 哈希表查找:O(1)