打开 LeetCode 的 Hash Table 标签,你会发现题量不少,但真正痛苦的往往不是题解看不懂,而是很多题目你跟着题解走觉得“就这?”,合上答案自己写又卡在同一个地方:到底什么时候该用哈希表?用哈希表的时候,Key 存什么?Value 又该存什么?如果你也卡在这几个问题上,这篇内容应该能帮你把哈希章节的“思路”和“实现”串成一条线。
这篇文章面向正在按 LeetCode 标签顺序刷哈希表题目的人,也适合准备面试前想重新整理查找、去重、计数、记忆化这几类问题的朋友。我会从暴力解的推导过程讲起,然后按题型给出可以直接复用的代码模板,再深入哈希设计题和一些进阶用法,比如“目标和”这道题里藏在 DFS 底下的状态哈希。所有代码都用 Java 写,换成 C++ 的 unordered_map 或 Python 的 dict 思路完全一样。
1. 哈希表在算法题里的真正角色:不是容器,是“历史检索”
1.1 哈希题大多可以浓缩成一个动作:查之前见过的信息
很多人以为哈希表这类题的核心是“会用 HashMap 这个 API”,其实不对。哈希表在算法题里几乎只有三类用途:去重、计数、关联映射。
去重解决的是“这个东西之前出现过吗”,比如判断字符串里有没有重复字符;计数解决的是“这个东西之前出现过几次”,比如统计每个词的频率;关联映射则是“这个东西之前对应的值是什么”,比如两数之和里需要根据差值找下标,又比如记忆化搜索里根据状态找已经算过的答案。
这三类用途本质上都有一个共同点:你在遍历数据的过程中,需要频繁回头查看历史信息。
如果题目里没有“回头查”的动作,哈希表大概率不是最优解。如果题目里有“回头查”的动作,那哈希表就可以列入候选方案。这个判断标准虽然简单,但特别管用。说实话,很多人哈希章节卡住,不是不会写 put 和 get,而是根本没想到“这个题需要把遍历过的信息留下来”,所以才会在双层循环里白白浪费时间。
1.2 数字题里不是每次都需要 HashMap,数组就是最原始的哈希表
哈希表章节中有一类题其实可以用数组完成,而且更快,比如“有效的字母异位词”。
这道题判断两个字符串的字母组成是否完全相同。如果只用小写字母,键的取值范围一共就 26 个。你当然可以写一个 HashMap<Character, Integer> 统
