"需要快速判断某个值是否出现过?" → 哈希。
"两个位置一起移动、避免重复枚举?" → 双指针。
"数组里找两数关系、去重、原地操作?" → 优先想 HashMap / Set / 双指针。
Part A|数组 + HashMap / Set
1. 数组到底是什么?
数组本质上是一段连续存储的同类型/同一逻辑集合数据。
python
nums = [1, 2, 3, 4]
python
按下标访问:O(1)
尾部 append:均摊 O(1)
中间插入:O(n)
中间删除:O(n)
查找某个值:O(n)
严格来说 Python list 不是传统意义上的固定长度数组,它更接近动态数组,但在 LeetCode 和机考中,我们直接把它当数组使用。
2.HashMap / Set 是什么?
python
d = {}
s = set()
dict → key : value
set → 只有 key
Hash 查找是平均 O(1),不是绝对 O(1)。
理论最坏情况下,由于哈希冲突等原因,性能可能退化。
什么时候应该想到哈希?
python
是否出现过
重复
频率
计数
两数之和
映射关系
第一次出现位置
字符统计
O(n) 内查找
尤其是:
"数组里找两个数满足某种关系。"
因为 Set 中不能存在重复元素。
所以 Set 最典型的用途就是:
去重 + 快速判断某个元素是否存在。
什么是 Hash Table?为什么查询快?
哈希表是一种通过哈希函数将 Key 映射到存储位置的数据结构,底层通常基于桶数组实现。
在查询一个 Key 时,首先计算 Key 的 hash 值,再根据 hash 值定位到对应的 bucket,而不需要像线性表一样从头遍历,因此在哈希分布比较均匀的情况下,查找、插入和删除的平均时间复杂度可以达到 O(1)。
但是不同 Key 可能映射到同一个位置,这叫哈希冲突。常见解决方式包括链地址法和开放寻址法。因此哈希表的 O(1) 一般指平均时间复杂度,极端冲突情况下性能可能退化。
为什么 HashSet 查询是 O(1)?
因为 HashSet 通常基于哈希表实现。查询元素时不是遍历 Set 中所有元素,而是先计算元素的 hash 值,通过 hash 值直接定位到对应的 bucket,再进行必要的比较,所以平均时间复杂度是 O(1)。
哈希表本质上干的事情,就是把"按内容查找"转换成了"算出位置后按位置查找"。
python
List 查找:
May 在哪?
↓
A → B → C → D → May
O(n)
HashSet 查找:
May 在哪?
↓
hash("May")
↓
bucket 7
↓
直接去 bucket 7
平均 O(1)
Part B|双指针
- 双指针到底是什么?
双指针严格来说不是数据结构,而是一种遍历技巧。
核心思想:
不让两个位置彼此独立地枚举,而是根据题目性质,让两个指针有规律地移动。
常见两类。
左右指针
python
L → ← R
python
有序数组
回文
两数之和
盛水
快慢指针
python
slow →
fast ----->
例如:
python
原地删除
移动零
链表环
去重
什么时候应该想到双指针?
python
有序数组
两个数满足某关系
原地修改
删除元素
去重
移动元素
首尾比较
回文
区间收缩
"要求 O(1) 额外空间,并原地修改数组"
尤其要想到:快慢指针
哈希练习:LeetCode 1. 两数之和
双指针练习:LeetCode 167. 两数之和 II
练习题
练习 1|LeetCode 217. 存在重复元素
练习 2|LeetCode 283. 移动零 快慢指针
核心思想:用一个指针 i记录"下一个非零元素应该放的位置",遍历数组把非零元素往前挪,最后把剩余位置补 0。
练习 3|LeetCode 49. 字母异位词分组 hint:同一组字符串拥有相同的 key
练习 4|LeetCode 15. 三数之和
机考视角
通常包装
python
订单ID是否重复
用户编号配对
设备记录去重
字符串统计
按照某规则找到两个元素
区间两端不断收缩
常见优化路线:
python
双循环 O(n²)
↓
HashMap / Set
↓
O(n)
python
数组排序
↓
左右双指针
python
是否有优化空间?
是否可以利用有序性 → 双指针
面试手撕训练|今天选:三数之和
最直接的方法是三重循环枚举三个元素,时间复杂度 O(n³),数据量大时不可接受。
我可以先对数组排序,然后固定第一个数字,把剩下的问题转换为有序数组的两数之和。对于剩余区间使用左右双指针,如果三数之和小于 0,则左指针右移;如果大于 0,则右指针左移。这样每固定一个元素,剩余部分只需要 O(n) 扫描,因此整体时间复杂度降低为 O(n²)。
这道题还需要重点处理重复答案,所以固定元素以及找到答案后,都需要跳过相同值。
Cheat Sheet|Day 1
HashMap / Set
python
看到这些想到哈希
出现过没有
重复
计数
频率
映射关系
两数之和
快速查找
Python
sql
seen = set()
if x in seen:
...
seen.add(x)
Dict:
d = {}
d[key] = value
if key in d:
...
复杂度通常:
python
查询:平均 O(1)
插入:平均 O(1)
删除:平均 O(1)
空间:
python
O(n)
双指针
python
左右指针识别信号
有序数组
首尾比较
两数关系
回文
区间收缩
模板:
python
left = 0
right = len(nums) - 1
while left < right:
if ...:
left += 1
else:
right -= 1
快慢指针识别信号
python
原地修改
删除
去重
移动元素
模板:
python
slow = 0
for fast in range(len(nums)):
if condition:
nums[slow] = nums[fast]
slow += 1
两个判断:
需要把"查找"从 O(n) 降下来 → 考虑 Hash。
两个变量存在单调关系,不需要所有组合都枚举 → 考虑双指针。