菜单

算法复杂度

相关源文件

算法复杂度是一个用于分析和比较算法效率的框架。它有助于衡量算法性能随输入大小增长的变化,这对于构建高效的软件系统至关重要。本页涵盖了渐近分析、大 O 符号的基础知识,以及如何在评估算法性能时应用这些概念。

有关具体算法实现的信息,请参阅数据结构算法

目的与范围

理解算法复杂度使软件工程师能够

  • 客观地评估和比较算法性能
  • 预测算法将如何随着数据量的增加而扩展
  • 在选择数据结构和算法时做出明智的决策
  • 识别并解决性能瓶颈
  • 在 Google、Amazon、Facebook 和 Microsoft 等公司的技术面试中取得成功

来源:README.md574-596

渐近分析基础

渐近分析通过衡量执行时间和内存使用随输入大小增加的增长情况来衡量算法效率。我们分析的是资源消耗的增长率,而不是测量绝对时间(这会因硬件而异)。

图:算法分析方法

渐近分析的关键概念

  • 关注增长率:算法性能如何随输入增加而扩展
  • 硬件独立性:分析独立于特定硬件或实现细节
  • 关注大输入:关注足够大的输入情况下的行为
  • 简化表示:忽略常数因子和低阶项

来源:README.md580-585

大 O 符号

大 O 符号描述了算法增长率的上限(最坏情况)。它表示为 O(f(n)),其中 f(n) 是输入大小 n 的函数。

常见复杂度类别

符号姓名描述示例
O(1)常量执行时间与输入大小无关数组访问,哈希表查找(最佳情况)
O(log n)对数级执行时间随输入大小呈对数增长二分查找,平衡二叉查找树
O(n)线性级执行时间随输入大小呈线性增长线性查找,遍历数组
O(n log n)线性对数线性增长和对数增长的组合高效排序算法(归并排序,快速排序平均情况)
O(n²)平方级执行时间随输入大小的平方增长冒泡排序,插入排序,嵌套循环
O(n³)立方执行时间随输入大小的立方增长一些矩阵操作,朴素的三维计算
O(2^n)指数级执行时间随每个附加输入元素呈指数增长斐波那契数列的递归计算,暴力解法
O(n!)阶乘执行时间随输入大小呈阶乘增长暴力旅行商问题,生成所有排列

来源:README.md582-591

复杂度类别比较

图:常见时间复杂度类别的层级结构(性能越好排在越上方)

来源:README.md589

其他渐近符号

虽然大 O 符号是最常用的符号,但还有其他重要的渐近符号

  1. 大 Ω (Omega):下界符号。描述最佳情况。

    • Ω(f(n)) 表示算法至少需要 f(n) 的时间/空间。
  2. 大 Θ (Theta):紧密界符号。描述算法的上限和下限相同的情况。

    • Θ(f(n)) 表示算法精确地需要 f(n) 的时间/空间(在常数因子内)。
  3. 小 o (little-o):严格上界。与大 O 类似,但排除了边界情况。

    • o(f(n)) 表示算法的增长严格慢于 f(n)。

图:渐近符号之间的关系

来源:README.md582-583

大 O 分析规则

分析算法复杂度时,请遵循以下规则

  1. 系数规则:忽略常数系数

    • O(2n) → O(n)
    • O(1/2n) → O(n)
  2. 求和规则:将独立操作的复杂度相加

    • O(n) + O(log n) → O(n)
    • 始终保留最高阶项
  3. 乘积规则:将相关操作的复杂度相乘

    • O(n) * O(n) → O(n²)
  4. 多项式规则:保留最高次幂项

    • O(n² + n) → O(n²)
    • O(n³ + n² + n + 1) → O(n³)
  5. 对数底数规则:对数底数在大 O 中无关紧要

    • O(log₂n) = O(log₁₀n) = O(log n)

来源:README.md580-589

常见算法分析示例

理解标准操作和算法的复杂度至关重要

图:常见数据结构操作及其复杂度

来源:README.md580-589

排序算法

算法最佳情况平均情况最坏情况空间复杂度
冒泡排序O(n)O(n²)O(n²)O(1)
选择排序O(n²)O(n²)O(n²)O(1)
插入排序O(n)O(n²)O(n²)O(1)
归并排序O(n log n)O(n log n)O(n log n)O(n)
快速排序O(n log n)O(n log n)O(n²)O(log n)
堆排序O(n log n)O(n log n)O(n log n)O(1)

来源:README.md113-118

空间复杂度

空间复杂度衡量算法使用的内存量,与输入大小相关。它遵循与时间复杂度相同的大 O 符号。

空间复杂度的关键组成部分

  • 辅助空间:算法使用的额外空间(不包括输入)
  • 输入空间:存储输入所用的空间

常见空间复杂度

  • O(1):常数空间,与输入大小无关(例如,迭代阶乘)
  • O(n):线性空间,与输入大小成比例(例如,复制数组)
  • O(log n):对数空间(例如,使用递归实现的二分查找)

图:空间复杂度分析的组成部分

来源:README.md516-517

摊还分析

摊还分析检查一系列操作的平均性能,即使某些单个操作可能代价高昂。

这对于动态数组(向量)等数据结构尤为重要,因为偶尔的重新分配操作虽然昂贵但很少发生。

示例:向动态数组添加元素

  • 大多数添加操作为 O(1)
  • 偶尔需要 O(n) 来复制所有元素以进行重新分配
  • 摊还成本随时间平均每次操作仍为 O(1)

来源:README.md585

在面试中应用复杂度分析

在技术面试中,算法复杂度分析对于展示您的工程严谨性和解决问题能力至关重要。

最佳实践

  1. 编码前分析:在实现之前讨论您方法的时空复杂度
  2. 考虑多种方法:根据复杂度比较不同的算法
  3. 理解权衡:认识何时为空间牺牲时间,反之亦然
  4. 识别瓶颈:找出算法中复杂度最高的部分
  5. 清晰表达:使用正确的大 O 符号并解释您的推理
  6. 考虑边缘情况:讨论复杂度如何随不同输入而变化
  7. 逐步改进:从一个可行的解决方案开始,然后优化其复杂度

来源:README.md594-595

该仓库推荐了几个学习算法复杂度的资源

视频资源

  • 哈佛 CS50 - 渐近符号
  • 大 O 符号教程
  • 加州大学伯克利分校关于大 O 的讲座
  • 摊还分析解释
  • TopCoder 计算复杂度文章

文本资源

  • 大 O 备忘单 (bigocheatsheet.com)
  • 《程序员面试金典》- 大 O 章节

所有这些资源都有助于建立对如何分析和比较基于资源使用模式的算法的扎实理解。

来源:README.md580-595

结论

算法复杂度是计算机科学中的一个基本概念,它允许工程师分析、比较和优化算法。通过理解大 O 符号和渐近分析,您可以更好地预测您的代码在不同输入大小下的表现,并就算法和数据结构的选择做出明智的决策。

这些知识在技术面试中尤其有价值,因为展示对算法效率的理解可以显著影响您的成功。在您继续学习数据结构和算法时,请始终如一地应用复杂度分析,以培养对不同方法性能特征的直觉。

来源:README.md574-596