哈希表(Hash Table)是算法面试中性价比最高的数据结构。它的核心优势在于能以的时间复杂度完成“查找”、“判断存在”和“统计频率”。
只要题目中出现 “快速判断某个元素是否出现过” 、 “统计某个元素出现的个数” 或 “寻找配对元素”,你首先应该想到的就是哈希法。
以下是针对 JavaScript/TypeScript 的哈希法通用模版和核心题库。
1. 通用解题模版 (JavaScript)
在 JS 中,我们主要使用三种结构来实现哈希法:
- 数组 (
Array):适用于 Key 范围小且连续(如a-z26个字母)。速度最快。 - 集合 (
Set):适用于只需要判断“在不在”,不需要存 Value 的场景。 - 映射 (
Map):适用于需要存 Key-Value 对(如:元素 -> 下标,元素 -> 出现次数)。
模版一:数组哈希 (处理字符串/有限范围)
适用场景:异位词、字符统计(Key 只有 26 个字母)。
// 示例:判断两个字符串字符计数是否相同
function arrayHash(s, t) {
if (s.length !== t.length) return false;
// 申请一个长度 26 的数组,索引 0-25 对应 a-z
const record = new Array(26).fill(0);
const base = 'a'.charCodeAt(0);
for (const char of s) {
// 利用 ASCII 码计算索引
record[char.charCodeAt(0) - base]++;
}
for (const char of t) {
const index = char.charCodeAt(0) - base;
record[index]--;
if (record[index] < 0) return false;
}
return true;
}
模版二:Set 哈希 (去重 / 存在性判断)
适用场景:找两个数组的交集、判断是否重复、快乐数。
// 示例:数组去重或判断存在
function setHash(nums) {
const set = new Set();
for (const num of nums) {
// 1. 判断是否存在
if (set.has(num)) {
return true; // 发现重复
}
// 2. 加入集合
set.add(num);
}
return false;
}
模版三:Map 哈希 (Key-Value 映射)
适用场景:两数之和、统计频率、前缀和优化。
// 示例:两数之和 (Two Sum)
function mapHash(nums, target) {
// Map 结构:Key 是数值,Value 是下标
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i]; // 需要寻找的另一半
// 查表:看看之前是否遍历过“另一半”
if (map.has(complement)) {
return [map.get(complement), i];
}
// 存表:把当前数和下标存进去
map.set(nums[i], i);
}
return [];
}
2. 力扣 (LeetCode) 核心题号
我按使用场景对高频题进行了分类。建议按顺序刷。
🟢 第一阶段:哈希基础 (必做)
熟悉 Array、Set 和 Map 的基本 API。
| 题号 | 题目名称 | 难度 | 场景与备注 |
|---|---|---|---|
| 1 | 两数之和 | Easy | 梦开始的地方。必背 Map 模版。 |
| 242 | 有效的字母异位词 | Easy | 数组哈希经典题。用 Array(26) 解决。 |
| 349 | 两个数组的交集 | Easy | 考察 Set的去重和查找能力。 |
| 202 | 快乐数 | Easy | 题目中暗含“无限循环”,意味着会出现重复的值,用 Set破局。 |
🟡 第二阶段:进阶应用 (高频)
结合字符串处理、滑动窗口等技巧。
| 题号 | 题目名称 | 难度 | 场景与备注 |
|---|---|---|---|
| 49 | 字母异位词分组 | Medium | 面试超高频。重点在于如何设计 Hash 的 Key(如将字符串排序作为 Key)。 |
| 454 | 四数相加 II | Medium | 其实是“两数之和”的升级版。将 A+B 的结果存 Map,再在 C+D 中找相反数。 |
| 383 | 赎金信 | Easy | 242 的变体,数组哈希的应用。 |
| 3 | 无重复字符的最长子串 | Medium | 必刷。哈希表 + 滑动窗口。用 Set 或 Map 记录窗口内的字符。 |
🔴 第三阶段:哈希 + 前缀和 / 巧妙设计 (难点)
这类题目通常不会直白地告诉你用哈希,需要转化思维。
| 题号 | 题目名称 | 难度 | 场景与备注 |
|---|---|---|---|
| 128 | 最长连续序列 | Medium | 也是Hot 100。利用 Set 去重,通过 查找来从中心向两边扩散。 |
| 560 | 和为 K 的子数组 | Medium | 经典难点。前缀和 + 哈希表。Map 存 {前缀和: 出现次数}。 |
| 525 | 连续数组 | Medium | 560 的变体。把 0 看作 -1,问题转化为“和为 0 的最长子数组”。 |
3. 刷题避坑指南 (针对 JS/Frontend)
- Object vs Map:
- 在刷算法题时,优先使用
Map。 - 因为
Map.size获取长度是 ,而 Object 需要Object.keys().length是 。 - Map 的 Key 可以是任意类型(数字、字符串),Object 的 Key 只能是字符串或 Symbol(会自动把数字转字符串,可能有坑)。
- 空间换时间:
- 哈希法的本质是牺牲空间换取时间。如果题目限制了空间复杂度(如 空间),通常不能用哈希表(可能要用双指针或位运算)。
- 如何设计 Key (针对 49 题等):
- 如果需要把“数组”或“对象”当作 Key 存入 Map,在 JS 中直接存是存引用的。
- 通常需要将它们序列化成字符串。例如:
['a', 'b']->"a,b"作为 Key。
建议路线:先搞定 1 -> 242 -> 49,这三道通了,哈希表基本就入门了。然后挑战 560,这是很多大厂(如字节、美团)的分水岭题目。