【面试资料】Java集合篇 之 集合概述、复杂度分析、底层数据结构

一. Java集合框架

在这里插入图片描述
其中,ArrayList / LinkedList + HashMap / ConcurrentHashMap 无疑是面试中出现频率最高的。

集合类的 底层数据结构 也有规律可循:

  • Array → 数组
  • Hash → 散列表(哈希表)
  • Linked → 链表
  • Tree → 红黑树

本文将会聚焦 Java集合类数据结构,储备一定的基础知识;

我们还要重点掌握 算法复杂度 分析方法 —— 评判性能优劣的最关键指标,从而帮助我们更好的认识与分析Java集合,在编程时更科学、更合理地选用集合。

更多Java集合类的 具体实现原理、源码分析 以及 常考面试题 请关注【面试资料】Java集合篇 的后续内容。

二. 算法复杂度

1. 时间复杂度(重点)

大O表示法:不具体表示代码真正的执行时间,而是代表 代码 执行时间 / 次数 随 数据规模(n)增长变化趋势

在处理大O表示法时,公式中的 低阶、常量、系数 三部分并不左右其增长趋势,因此可以忽略,我么只需要记录 最大的量级 即可。
在这里插入图片描述


【典例1】

在这里插入图片描述
评判时间复杂度的标准是 时间随 n 增长 的变化趋势

以上代码虽然使用了for循环执行累加操作100次,但是无论n多大,执行的代码总是固定的,所以时间复杂度无疑是 O(1)


【典例2】

在这里插入图片描述
我们再次强调:复杂度分析就是弄清楚代码的执行次数和n之间的关系。

以上代码我们需要寻找 while循环的变量 i数据规模 n 的数量关系;由于 i 是 跳跃增长(i = i * 2) 的,我们可以使用 枚举 的方法辅助寻找它们之间的关系。
在这里插入图片描述
经过以上的分析,我们可以很明确的看出时间复杂度为 O(log n),注意:该时间复杂度 与底无关


【典例3】

在这里插入图片描述
以上代码是 调用关系,而非平行关系,所以 “只取最大量级” 的原则并不适用于此,两者应该 相乘,所以时间复杂度应为 O(n * log n)


2. 空间复杂度

空间复杂度的表示方法依然是 大O表示法,只不过是在表示 “额外存储空间 与 数据规模n 之间的增长关系”。对于空间复杂度,常见的有O(1) / O(n) / O(n ^ 2)。

举例说明:
在这里插入图片描述
左侧代码只是在 代码执行次数 上受到了 数据规模n 的影响,时间复杂度为 O(n),但是 空间复杂度为 O(1),并没有额外开辟空间。

右侧代码不仅以n为循环条件,并且额外开辟了 长度为n 的 数组a,所以右侧代码的 时间 / 空间复杂度皆为 O(n)

【注意】
在实际编程中,通常会选择 【空间换时间】,最典型的就是动态规划算法 —— 额外开辟DP数组,寻找状态转移方程,用于代替低效的递归搜索、暴力搜索。牺牲一定的空间,但即使是开辟了 O(n ^ 2) 的二维数组,执行效率也会十倍百倍增加,并且优化效果会随数据规模的变大越发明显。

三. 数据结构

对于 单列集合,最常见的底层数据结构必然是数组 与 链表

对于 双列集合,特别是HashMap,底层数据结构涉及散列表(数组 + 链表)与 红黑树

【注意】在此我们重点关注各类数据结构在 执行操作时的时间复杂度,并不会详细介绍操作的具体步骤。

1. 数组

① 定义:一种 连续的内存空间 存储 相同数据类型 数据的 线性 数据结构。
在这里插入图片描述
寻址公式:a[ i ] = baseAddress + i * dataTypeSize【第 i 个元素地址 = 首地址 + 下标 × 类型大小】

面试题:为什么数组索引从0开始?加入从1开始不行吗?
回答:
① 使用数组下标获取元素的时候,我们会用寻址公式来计算元素的内存位置,寻址公式是:首地址 + 下标 × 类型大小;
② 如果索引从1开始,每次寻址CPU都需要额外执行一次减法操作,性能不高。
在这里插入图片描述

数组操作 的 时间复杂度

  • 索引查询:使用 数组下标 i 配合 寻址公式 直接定位元素地址,时间复杂度O(1)
  • 无序数组查询:只能遍历数组寻找元素具体位置,时间复杂度O(n)
  • 有序数组查询:经过排序的数组可以使用二分查找,时间复杂度O(log n)
  • 插入 / 删除:插入删除后保证内存连续性,需要移动元素,时间复杂度O(n)

2. 链表

(1)单向链表

① 定义:线性表的链式存储,它是通过一组 任意的存储单元 来存储线性表中的数据元素,每个结点的 后继指针 指向下一个结点的位置。
在这里插入图片描述
单链表操作 的 时间复杂度

  • 查询:查询 头结点 为 O(1)其他结点均为 O(n)
  • 访问:已定位到某结点,访问其 后继结点 为 O(1)前驱结点 为 O(n)
  • 插入 / 删除:单链表在执行插 / 删操作之前必须先定位,所以仍然涉及查询操作,故插 / 删 头结点 为 O(1)其他结点均为 O(n);若已给定结点,后插 / 删除 为 O(1)前插 为 O(n)
(2)双向链表

① 定义:在单链表的基础上,每个结点增设一个 前驱指针,指向前驱结点。
在这里插入图片描述
双链表操作 的 时间复杂度

  • 查询:查询 头尾结点 为 O(1)其他结点均为 O(n)
  • 访问:已定位到某结点,访问其 前驱 / 后继结点 皆为 O(1)
  • 插入 / 删除:同单链表,执行前先定位,所以插 / 删 头尾结点 为 O(1)其他结点均为 O(n);若已给定结点,前插 / 后插 / 删除 皆为 O(1)

3. 二叉树

① 特点:二叉树是一种特殊的树形结构,每个结点 至多 拥有两棵子树,并且左右子树有顺序,不可颠倒

② 存储方式:

  • 顺序存储 结构:使用数组存储各结点,高度为 h最坏单支树需要开辟 2^h - 1 个存储单元
  • 链式存储 结构:为了解决顺序存储空间利用率低的问题,二叉树通常都会使用链式存储。
    在这里插入图片描述
    在这里插入图片描述

二叉搜索树 BST:在构造时,遵循 【左小右大】 的插入原则。

BST在进行 插入 / 删除 / 查找 时的 平均时间复杂度皆为 O(log n)

但是对于 “最坏单支数”时间复杂度 将退化为 单链表 O(n)

👉 了解更多关于 “BST、AVL、红黑树、B树、B+树” 的知识,推荐阅读【面试资料】MySQL篇 之 索引 的内容。

4. 红黑树

① 特点:满足BST的“左小右大”原则,根结点保持黑色;不存在两个相邻的红色结点,一个结点到其所在子树的叶结点的路径上,黑色结点的数量都相同。【左根右,根叶黑。不红红,黑路同。】

数据结构 的 关联:
为了解决BST的 高度差过大 问题 → AVL
为了解决AVL的 调整太频繁 问题 → 红黑树

红黑树操作 的 时间复杂度
结合BST的特点,并且结合数据结构之间的关联,我们可以得出一些结论:对于 插入 / 删除 / 查找,红黑树避免了 “最坏单支数” 的情况,所以整体复杂度稳定在 O(log n)

⭐ 对于链式存储的树形结构,插入 / 删除 操作都要基于 查找操作 之上,所以时间复杂度需要 O(log n),而 插入 / 删除 以及 旋转调整操作 本身的时间复杂度仅为 O(1),这与单链表非常相似。

5. 散列表(哈希表)

① 特点:散列表 建立了 关键字key存储地址 之间的一种 直接映射关系,由数组演化而来;我们不妨对比

  • 数组:通过 下标 定位地址,访问 数据
  • 散列表:通过 key 定位地址,访问 value

② 散列函数:数组有寻址公式,散列表也有自己的散列函数 —— “一个把关键字key 映射成 该关键字的地址 的函数”,记为 Hash(key) = Addr。

③ 散列函数的基本要求:

  • 【全面】散列函数的定义域必须 包含全部 需要存储的关键字;
  • 【均匀】散列函数计算出来的地址应该能 均匀分布 在整个地址空间中,减少冲突
  • 【简单】散列函数应该 尽量简单,在较短时间内计算出关键字对应的散列地址。

④ 散列冲突:

散列冲突,又称 哈希碰撞,指 多个key 映射到 同一块地址

我们可以通过设计合理的散列函数 减少 实现均匀存储,减低冲突的频率,但是不可能完全避免。所以散列表必须拥有处理散列冲突的策略。

常见的冲突处理方法有 “开放定址法、拉链法” 等,其中最常用的,也是HashMap底层使用的策略就是 拉链法

⑤ 拉链法:
在这里插入图片描述
“散列表 + 拉链法” 操作 的 时间复杂度

  • 插入:通过散列函数定位,直接执行 头插链表 即可,时间复杂度 O(1)
  • 删除 / 查找:理想情况下数据足够分散,时间复杂度可以达到 O(1);但是当哈希冲突频繁、数据量过大时,散列表可能会退化为链表,从而 定位工作 的 时间复杂度为 O(n)
  • 优化:为了防止上述情况的发生,在一定条件下,HashMap会把链表 转换为 红黑树,使 插入 / 删除 / 查找 的效率皆稳定在 O(log n)

具体实现细节请关注HashMap的面试资料。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值