1. 哈希表的核心作用
哈希表(字典/集合)的本质是 「用空间换时间」,解决以下三类问题:
? 快速查找元素是否存在(如两数之和的补数、最长序列的下一个数)
? 快速统计元素特征(如字母异位词的统一键、元素出现次数)
? 避免重复计算(如最长序列的起点判断、子数组和问题)
2. 哈希题三大特征(出现即用哈希表)
当题目出现以下特征时,优先考虑哈希表:
1?? 需要频繁检查某元素是否已存在
- 例:两数之和找补数、最长连续序列找下一个数
2?? 需要将元素按某种规则归类 - 例:字母异位词分组、相同频率数归类
3?? 要求时间复杂度 O(n) 且暴力法为 O(n2) - 例:找重复数、找缺失数、统计出现次数
3. 哈希表解法模板
无论题目如何变化,代码结构都可归纳为以下三步:
def hash_problem(data):
# 1. 预处理:建立哈希表(字典/集合)
hash_map = {} # 或 hash_set = set()
# 2. 遍历数据,动态更新哈希表
for item in data:
if 条件1: # 如「补数是否存在」「是否可归类」
return 结果 # 找到解立即返回
更新哈希表 # 如存入当前值/统计次数/设计键
# 3. 最终处理(如返回统计结果)
return 结果
4. 具体到你的三题,如何抽象共性?
题目 | 哈希表用途 | 触发条件 | 代码模板位置 |
---|---|---|---|
两数之和 | 存储已遍历的数,快速查补数 | 补数 in hash_map | 步骤2的条件判断 |
字母异位词分组 | 用排序后的字符串作为统一键 | key = sorted(s) | 步骤2的更新哈希表 |
最长连续序列 | 存所有数,只处理序列起点 | num-1 not in hash_set | 步骤2的条件判断 |
共性抽象:
- 核心操作:都依赖「快速判断元素是否存在」
- 避免冗余:通过预处理或条件判断减少计算量(如最长序列只处理起点)
- 空间换时间:用额外空间将时间复杂度从 O(n2) 降到 O(n)
5. 实战训练:新题如何联想哈希表?
题目:「给定数组,找到所有和为k的子数组数量」
思考过程:
1?? 暴力法:双重循环遍历所有子数组,计算和 → O(n2)
2?? 哈希联想:需要快速判断「当前前缀和 - k」是否出现过 → 用字典存前缀和出现次数
3?? 解法:
- 字典记录
{前缀和: 出现次数}
- 遍历时计算当前前缀和,检查
前缀和 - k
是否在字典中
代码:
def subarraySum(nums, k):
prefix_sum = {0: 1} # 初始前缀和为0出现1次
current_sum = 0
count = 0
for num in nums:
current_sum += num
if current_sum - k in prefix_sum: # 关键判断
count += prefix_sum[current_sum - k]
prefix_sum[current_sum] = prefix_sum.get(current_sum, 0) + 1
return count
6. 终极记忆口诀
- 要查有无,必用哈希 → 快速判断元素存在性
- 归类统计,设计键值 → 如异位词排序、前缀和统计
- 空间换时间,O(n) 必杀技 → 暴力超时就上哈希
总结:哈希表不是“技巧”,而是对暴力法的降维打击。当你发现代码中有嵌套循环(O(n2)),且内层循环在查找/统计时,立刻想到哈希表!