时间复杂度 & 空间复杂度

时间复杂度评估算法运行需要多少时间;空间复杂度评估算法运行需要占用多少内存。

两者都只关心【数据规模变大时】的增长趋势,不统计精确耗时 / 内存大小。

  • 时间复杂度:算循环多少次
  • 空间复杂度:算新开多少内存

一、时间复杂度 O (f (n))

n:输入数据的规模(比如数组长度) \(O()\):大 O 记号,表示最坏情况下,算法执行步数随 n 的增长量级,忽略常数、低次项。

常见复杂度(从快到慢)

  1. (O(1)) 常数阶:执行次数固定,和 n 无关
java 复制代码
int a = arr[0]; // 不管数组多大,只取第一个元素
  1. (O(log n)) 对数阶:每次处理把问题规模减半,二分查找
  2. (O(n)) 线性阶:循环遍历一遍数据
java 复制代码
for(int i=0; i<n; i++){}
  1. (O(nlog n)):线性对数阶,快速排序、归并排序

  2. (O(n^2)) 平方阶:两层嵌套循环

    for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){}
    }

  3. (O(n^3)) 立方阶:三层循环

  4. (O(2^n)) 指数阶、(O(n!)) 阶乘阶:数据稍微大一点就直接算不完,基本不可用

✅ 重点:

  • 只看最高阶项,去掉系数。例:(2n^2+3n+5) → (O(n^2))
  • 默认分析最坏时间复杂度

二、空间复杂度 O (f (n))

衡量算法额外申请的内存空间 随数据规模 n 的增长趋势。 ⚠️ 输入本身占用的空间不算,只算算法执行过程中新开辟的变量、数组、递归栈等。

例子

  1. (O(1)) 常数空间:只定义几个临时变量,没有开辟随 n 变大的容器

    int sum = 0;
    for(int i=0;i<n;i++) sum += arr[i];

  2. (O(n)):新创建长度为 n 的数组

    int[] newArr = new int[n];

  3. 递归:递归深度决定空间复杂度。比如递归深度为 n,则空间复杂度 \(O(n)\)

常见概念:原地算法(in-place),一般指空间复杂度 \(O(1)\),不需要额外大量存储空间。

三、举个直观对比例子

给定长度为 n 的数组,求数组最大值:

复制代码
int max = arr[0];
for(int i=1; i<n; i++){
    if(arr[i] > max) max = arr[i];
}
  • 时间复杂度:\(O(n)\),遍历一次数组
  • 空间复杂度:\(O(1)\),只额外开了 max 变量

四、面试常问要点

  1. 为什么不用毫秒统计运行时间?

代码运行时间受 CPU、语言、环境影响。复杂度是算法本身的理论特性,和机器无关,用来比较算法优劣。

  1. 时间和空间经常可以取舍:空间换时间(缓存,哈希表)。
  2. 大 O 只看增长趋势:\(n=10\) 的时候 \(O(n^2)\) 可能比 \(O(n)\) 还快;只有 n 很大的时候,复杂度差异才会体现。

Java 集合常用操作时间 & 空间复杂度

说明:

  1. 均为平均情况 ,标注最坏;n 代表集合内元素数量
  2. 只看核心操作:增、删、查
  3. 空间复杂度:指集合本身存储元素的空间,新增元素时底层扩容会有额外开销;临时操作一般是 O (1)

一、List 列表

ArrayList(数组实现,随机访问强)

操作 时间复杂度 说明
get(index) O(1) 直接数组下标访问
set(index) O(1) 修改指定下标
add (末尾) O (1) 平均,O (n) 最坏 扩容时需要复制整个数组
add (中间 / 头部) O(n) 后面元素全部后移
remove(index) O(n) 删除点之后元素全部前移
contains() O(n) 需要遍历查找

空间:底层数组容量 ≥ 元素个数;空间复杂度 O (n)

LinkedList(双向链表)

操作 时间复杂度 说明
get(index) O(n) 需要从头 / 尾遍历到下标
add (首尾) O(1) 链表节点修改指针
add (中间) O(n) 先要遍历找到位置
remove (首尾) O(1)
remove (中间) O(n) 先遍历定位
contains() O(n) 遍历查找

⚠️ 很多人误区:LinkedList 不是所有 add/remove 都是 O (1),只有头尾操作才是 O (1)

空间:O (n),每个节点额外存前后指针,内存开销比 ArrayList 大


二、Set 集合(不重复)

HashSet(底层 HashMap)

操作 时间复杂度
add O (1) 平均,O (n) 最坏(哈希大量冲突)
remove O (1) 平均,O (n) 最坏
contains O (1) 平均,O (n) 最坏

TreeSet(底层 TreeMap,红黑树,有序)

操作 时间复杂度
add / remove / contains \(O(\log n)\)

红黑树,元素自动排序;支持范围查找
LinkedHashSet:继承 HashSet,额外维护插入顺序;增删查平均 O (1)

空间:HashSet / TreeSet 都是 O (n)


三、Map 键值对

HashMap(面试重点!数组 + 链表 / 红黑树,JDK1.8)

操作 时间复杂度
get(key) O (1) 平均;最坏 O (n)(哈希冲突严重,链表)
put(key,val) O (1) 平均;最坏 O (n);扩容时 O (n)
remove(key) O (1) 平均;最坏 O (n)

JDK1.8:链表长度≥8 转红黑树,此时最坏退化成 \(O(\log n)\)

TreeMap(红黑树,key 有序)

操作 时间复杂度
put / get / remove \(O(\log n)\)

LinkedHashMap

底层 HashMap + 双向链表,保留插入顺序;增删查平均 O (1)

空间:HashMap / TreeMap O (n)


四、Queue / Deque

  1. ArrayDeque (数组双端队列,推荐替代 Stack、LinkedList 做队列)
    • addFirst/addLast、pollFirst/pollLast:平均 O (1)
    • 扩容时 O (n)
  2. PriorityQueue 优先队列(最小堆)
    • offer / poll:\(O(\log n)\)
    • peek:O(1)
    • contains:O(n)

✅ 总结

  1. ArrayList:随机访问 O (1);中间插入删除 O (n)
  2. LinkedList:头尾增删 O (1);按下标查找 O (n)
  3. HashMap:增删查平均 O (1);TreeMap 固定 \(O(\log n)\)
  4. HashSet 等价 HashMap;TreeSet 等价 TreeMap
  5. PriorityQueue 入队出队都是 \(O(\log n)\)
相关推荐
多弗朗皮卡丘1 小时前
数据结构番外:环形队列
数据结构·c
hahaha60161 小时前
黑体轨迹和色温--光源白点
人工智能·嵌入式硬件·算法·计算机视觉
嵌入式学习菌1 小时前
ESP32 ModbusTCP 分片缓存
java·后端·spring
海绵宝宝转agent1 小时前
基于Redis ZSet+AOP+注解实现限流注解算法
数据库·redis·算法
vx_Biye_Design1 小时前
springboot宠物寄养服务预约与监管系统82684-计算机课程设计、毕业设计
java·vue.js·spring boot·elasticsearch·课程设计·express·宠物
程序员小杰@1 小时前
Spring Boot 常用注解分类速记
java·spring boot·后端
vx_Biye_Design1 小时前
springboot小学生英语学习APP62773-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·学习·课程设计
吴声子夜歌1 小时前
Nginx应用与运维——Nginx日志管理
java·运维·nginx