Loveyless/Algo

★ 0Forks 0JavaScriptGitHub ↗Compare

README

Algo

算法学习仓库。


算法复杂度指南

什么是复杂度?

复杂度是衡量算法效率的指标,描述算法消耗的资源(时间或空间)如何随输入规模增长。


时间复杂度

定义:算法执行所需的基本操作次数,用大 O 表示法描述其增长趋势。

如何计算:

  1. 找出基本操作(比较、赋值、算术运算等)
  2. 统计操作次数与输入规模 n 的关系
  3. 保留最高阶项,忽略常数系数

常见复杂度(从快到慢):

复杂度 名称 示例 n=1000 时操作数
O(1) 常数 数组取值、哈希查找 1
O(log n) 对数 二分查找 ~10
O(n) 线性 遍历数组 1,000
O(n log n) 线性对数 快排、归并排序 ~10,000
O(n²) 平方 冒泡排序、双重循环 1,000,000
O(2ⁿ) 指数 暴力递归斐波那契 天文数字

计算示例:

// O(1) - 常数时间
function getFirst(arr) {
  return arr[0];  // 无论数组多大,只执行一次
}

// O(n) - 线性时间
function sum(arr) {
  let total = 0;
  for (let i = 0; i < arr.length; i++) {  // 循环 n 次
    total += arr[i];
  }
  return total;
}

// O(n²) - 平方时间
function bubbleSort(arr) {
  for (let i = 0; i < arr.length; i++) {       // 外层 n 次
    for (let j = 0; j < arr.length - 1; j++) { // 内层 n 次
      // ...                                    // 总共 n × n = n²
    }
  }
}

// O(log n) - 对数时间
function binarySearch(arr, target) {
  let left = 0, right = arr.length - 1;
  while (left <= right) {
    // 每次循环砍掉一半,所以是 log₂(n) 次
    let mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) return mid;
    if (arr[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return -1;
}

空间复杂度

定义:算法执行过程中额外占用的内存空间。

如何计算:

  1. 统计额外创建的变量、数组、对象
  2. 递归算法需计算调用栈深度

常见情况:

复杂度 场景
O(1) 只用几个变量,原地操作
O(n) 创建与输入等长的数组
O(log n) 递归深度为 log n(如二分)
O(n) 递归深度为 n(如链表递归)

示例:

// O(1) 空间 - 原地交换
function swap(arr, i, j) {
  let temp = arr[i];  // 只用了一个临时变量
  arr[i] = arr[j];
  arr[j] = temp;
}

// O(n) 空间 - 创建新数组
function double(arr) {
  const result = [];  // 新数组长度与输入相同
  for (let x of arr) {
    result.push(x * 2);
  }
  return result;
}

// O(n) 空间 - 递归调用栈
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1);  // 递归 n 层,栈深度 O(n)
}

最好/最坏/平均情况

同一算法在不同输入下表现可能不同:

情况 含义 快排示例
最好 理想输入下的复杂度 O(n log n),每次均匀分割
最坏 最差输入下的复杂度 O(n²),每次选到最大/最小值
平均 随机输入的期望复杂度 O(n log n)

实践建议:面试和工程中通常关注最坏情况,因为它决定了算法的可靠性。


稳定性(排序算法专属)

定义:相等元素排序后是否保持原有相对顺序。

原数组:[{name: "A", age: 20}, {name: "B", age: 20}]
按 age 排序后:
  稳定:A 仍在 B 前面
  不稳定:A、B 顺序可能交换
稳定排序 不稳定排序
冒泡、插入、归并 快排、堆排、选择

如何分析一个算法?

  1. 看循环:单层循环 O(n),嵌套循环相乘
  2. 看递归:画递归树,统计节点数
  3. 看分治:用主定理 T(n) = aT(n/b) + f(n)
  4. 看数据结构:哈希 O(1),树 O(log n),数组遍历 O(n)

复杂度速查表

算法 时间(平均) 时间(最坏) 空间 稳定
冒泡排序 O(n²) O(n²) O(1) ✓
选择排序 O(n²) O(n²) O(1) ✗
插入排序 O(n²) O(n²) O(1) ✓
快速排序 O(n log n) O(n²) O(log n) ✗
归并排序 O(n log n) O(n log n) O(n) ✓
堆排序 O(n log n) O(n log n) O(1) ✗
二分查找 O(log n) O(log n) O(1) -
哈希查找 O(1) O(n) O(n) -

Contributors

Loveyless

Issues