在数据结构中,复杂度(通常指时间复杂度和空间复杂度)是衡量算法效率的核心指标,用于分析算法在不同输入规模下的资源消耗情况。
1. 时间复杂度(Time Complexity)
衡量算法执行所需的操作次数(或时间)随输入规模 ( n ) 的增长趋势。常用 大O符号(O) 表示最坏情况下的上界。
常见时间复杂度(从低到高):
- O(1) :常数时间
- 操作与输入规模无关(如数组访问
arr[0])。
- 操作与输入规模无关(如数组访问
- O(log n) :对数时间
- 每次操作将问题规模减半(如二分查找、平衡二叉树操作)。
- O(n) :线性时间
- 操作次数与输入规模成正比(如遍历数组、链表)。
- O(n log n) :线性对数时间
- 分治算法的典型复杂度(如归并排序、快速排序)。
- O(n²) :平方时间
- 嵌套循环(如冒泡排序、选择排序)。
- O(2ⁿ) :指数时间
- 暴力搜索(如子集生成)。
- O(n!) :阶乘时间
- 全排列问题(如旅行商问题的暴力解法)。
示例:
- 线性搜索:O(n)(最坏需遍历整个数组)。
- 二分查找:O(log n)(每次排除一半元素)。
- 快速排序:平均 O(n log n),最坏 O(n²)(当数组已有序时)。
2. 空间复杂度(Space Complexity)
衡量算法执行过程中占用的额外存储空间随输入规模 ( n ) 的增长趋势。
常见空间复杂度:
- O(1) :常数空间
- 仅使用固定数量的变量(如交换两个数的值)。
- O(n) :线性空间
- 需要存储与输入规模成正比的数据(如哈希表、递归栈)。
- O(n²) :平方空间
- 存储二维数据结构(如动态规划的二维表格)。
示例:
- 迭代实现:通常 O(1) 空间(如迭代版斐波那契数列)。
- 递归实现:可能 O(n) 空间(如递归版斐波那契数列,因递归栈深度为 ( n ))。
- 归并排序:O(n) 空间(需额外数组存储中间结果)。
3. 复杂度分析的关键点
- 最坏情况 vs 平均情况 :
- 大O符号通常表示最坏情况(如快速排序的 O(n²)),但有时也分析平均情况(如快速排序的 O(n log n))。
- 忽略常数和低阶项 :
- O(3n + 2) 简化为 O(n),O(n² + n) 简化为 O(n²)。
- 递归算法的复杂度 :
- 使用递归树或主定理(Master Theorem)分析(如二分查找的递归式 ( T(n) = 2T(n/2) + O(1) ) 解为 O(n log n))。
4. 数据结构与复杂度的关系
不同数据结构的操作复杂度差异显著,选择时需权衡:
| 操作 | 数组 | 链表 | 哈希表 | 二叉搜索树 | 堆(优先队列) |
|---|---|---|---|---|---|
| 访问元素 | O(1) | O(n) | O(1) | O(log n) | O(1)(取极值) |
| 插入/删除 | O(n) | O(1) | O(1) | O(log n) | O(log n) |
| 搜索 | O(n) | O(n) | O(1) | O(log n) | O(n) |
5. 实际应用中的优化
- 时间换空间:如用哈希表缓存结果(空间 O(n) 换搜索 O(1))。
- 空间换时间:如动态规划预计算子问题(空间 O(n²) 换时间 O(n²))。
- 算法选择:根据问题规模选择合适算法(如小规模用插入排序 O(n²),大规模用快速排序 O(n log n))。
总结
- 时间复杂度:关注操作次数随 ( n ) 的增长趋势。
- 空间复杂度:关注额外存储空间的需求。
- 选择数据结构:根据操作频率(如频繁搜索用哈希表,频繁插入用链表)。
以下是常见数据结构及其核心操作的 时间复杂度 和 空间复杂度 的整理表格,按 线性表、串、堆栈、树、图 分类,方便直观记忆:
1. 线性表(数组 & 链表)
| 操作 | 数组(静态) | 数组(动态扩容) | 单链表 | 双向链表 |
|---|---|---|---|---|
| 访问元素 | O(1) | O(1) | O(n) | O(n) |
| 搜索元素 | O(n) | O(n) | O(n) | O(n) |
| 头部插入 | O(n)(需移动) | O(n)(可能扩容) | O(1) | O(1) |
| 尾部插入 | O(1) | O(1)(均摊) | O(n) | O(1) |
| 任意位置插入 | O(n) | O(n) | O(n) | O(n) |
| 头部删除 | O(n)(需移动) | O(n)(可能扩容) | O(1) | O(1) |
| 尾部删除 | O(1) | O(1)(均摊) | O(n) | O(1) |
| 空间复杂度 | O(n) | O(n)(可能扩容) | O(n) | O(n) |
2. 串(字符串)
| 操作 | 朴素匹配(Brute-Force) | KMP算法 | Boyer-Moore算法 |
|---|---|---|---|
| 搜索子串 | O(n×m)(最坏) | O(n+m) | O(n/m)(最好) |
| 空间复杂度 | O(1) | O(m) | O(m)(预处理表) |
注:
- ( n ) 为主串长度,( m ) 为子串长度。
- KMP 和 Boyer-Moore 是高效字符串匹配算法。
3. 堆栈(Stack)
| 操作 | 数组实现 | 链表实现 |
|---|---|---|
| 压栈(Push) | O(1)(均摊) | O(1) |
| 弹栈(Pop) | O(1) | O(1) |
| 查看栈顶 | O(1) | O(1) |
| 空间复杂度 | O(n) | O(n) |
4. 队列(Queue)
| 操作 | 数组实现(循环队列) | 链表实现 |
|---|---|---|
| 入队(Enqueue) | O(1)(均摊) | O(1) |
| 出队(Dequeue) | O(1) | O(1) |
| 查看队首 | O(1) | O(1) |
| 空间复杂度 | O(n) | O(n) |
5. 树(Tree)
二叉搜索树(BST)
| 操作 | 平均情况 | 最坏情况(退化为链表) |
|---|---|---|
| 搜索 | O(log n) | O(n) |
| 插入 | O(log n) | O(n) |
| 删除 | O(log n) | O(n) |
| 空间复杂度 | O(n) | O(n) |
平衡二叉搜索树(AVL/红黑树)
| 操作 | 所有情况 |
|---|---|
| 搜索 | O(log n) |
| 插入 | O(log n) |
| 删除 | O(log n) |
| 空间复杂度 | O(n) |
堆(优先队列)
| 操作 | 二叉堆(数组实现) |
|---|---|
| 插入(Insert) | O(log n) |
| 删除极值(Extract-Min/Max) | O(log n) |
| 查看极值 | O(1) |
| 建堆(Heapify) | O(n) |
| 空间复杂度 | O(n) |
6. 图(Graph)
邻接矩阵表示
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 访问顶点 | O(1) | O(n²) |
| 访问边 | O(1) | |
| 添加/删除顶点 | O(n) | |
| 添加/删除边 | O(1) |
邻接表表示
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 访问顶点 | O(1) | O(n + m) |
| 访问边 | O(deg(v))(顶点 ( v ) 的度) | |
| 添加/删除顶点 | O(1)(链表实现) | |
| 添加/删除边 | O(1)(链表实现) |
图遍历算法
| 算法 | 时间复杂度(邻接表) | 空间复杂度 |
|---|---|---|
| DFS(深度优先) | O(n + m) | O(n)(递归栈) |
| BFS(广度优先) | O(n + m) | O(n)(队列) |
最短路径算法
| 算法 | 时间复杂度(邻接表) | 空间复杂度 |
|---|---|---|
| Dijkstra(普通队列) | O(nm)(最坏) | O(n) |
| Dijkstra(优先队列) | O((n+m) log n) | O(n) |
| Bellman-Ford | O(nm) | O(n) |
| Floyd-Warshall | O(n³) | O(n²) |
最小生成树算法
| 算法 | 时间复杂度(邻接表) | 空间复杂度 |
|---|---|---|
| Prim(普通队列) | O(nm)(最坏) | O(n) |
| Prim(优先队列) | O((n+m) log n) | O(n) |
| Kruskal | O(m log m)(排序边) | O(n + m) |
总结表格(精简版)
| 数据结构 | 核心操作 | 时间复杂度(平均/最坏) | 空间复杂度 |
|---|---|---|---|
| 数组 | 访问/修改 | O(1) | O(n) |
| 链表 | 插入/删除(头部) | O(1) | O(n) |
| 二叉搜索树 | 搜索/插入/删除 | O(log n)/O(n) | O(n) |
| 平衡树 | 搜索/插入/删除 | O(log n) | O(n) |
| 堆 | 插入/删除极值 | O(log n) | O(n) |
| 哈希表 | 搜索/插入/删除 | O(1)(均摊) | O(n) |
| 图(邻接表) | 遍历(DFS/BFS) | O(n + m) | O(n) |
| Dijkstra | 最短路径 | O((n+m) log n) | O(n) |
记忆技巧
- 线性结构(数组/链表):操作与 ( n ) 成正比(O(n) 或 O(1))。
- 树结构:平衡时为 O(log n),退化为链表时为 O(n)。
- 图算法:遍历和最短路径通常为 O(n + m),优先队列优化可降复杂度。
- 哈希表:均摊 O(1),但需考虑哈希冲突。