算法系列(一)——哈希表

13 阅读4分钟

哈希表(Hash Table)是算法面试中性价比最高的数据结构。它的核心优势在于能以O(1)O(1)的时间复杂度完成“查找”、“判断存在”和“统计频率”。

只要题目中出现 “快速判断某个元素是否出现过”“统计某个元素出现的个数”“寻找配对元素”,你首先应该想到的就是哈希法。

以下是针对 JavaScript/TypeScript 的哈希法通用模版和核心题库。


1. 通用解题模版 (JavaScript)

在 JS 中,我们主要使用三种结构来实现哈希法:

  1. 数组 (Array ):适用于 Key 范围小且连续(如 a-z 26个字母)。速度最快。
  2. 集合 (Set ):适用于只需要判断“在不在”,不需要存 Value 的场景。
  3. 映射 (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四数相加 IIMedium其实是“两数之和”的升级版。将 A+B 的结果存 Map,再在 C+D 中找相反数。
383赎金信Easy242 的变体,数组哈希的应用。
3无重复字符的最长子串Medium必刷。哈希表 + 滑动窗口。用 Set 或 Map 记录窗口内的字符。

🔴 第三阶段:哈希 + 前缀和 / 巧妙设计 (难点)

这类题目通常不会直白地告诉你用哈希,需要转化思维。

题号题目名称难度场景与备注
128最长连续序列Medium也是Hot 100。利用 Set 去重,通过 查找来从中心向两边扩散。
560和为 K 的子数组Medium经典难点。前缀和 + 哈希表。Map 存 {前缀和: 出现次数}
525连续数组Medium560 的变体。把 0 看作 -1,问题转化为“和为 0 的最长子数组”。

3. 刷题避坑指南 (针对 JS/Frontend)

  1. Object vs Map:
  • 在刷算法题时,优先使用 Map
  • 因为 Map.size 获取长度是 ,而 Object 需要 Object.keys().length 是 。
  • Map 的 Key 可以是任意类型(数字、字符串),Object 的 Key 只能是字符串或 Symbol(会自动把数字转字符串,可能有坑)。
  1. 空间换时间:
  • 哈希法的本质是牺牲空间换取时间。如果题目限制了空间复杂度(如 空间),通常不能用哈希表(可能要用双指针或位运算)。
  1. 如何设计 Key (针对 49 题等):
  • 如果需要把“数组”或“对象”当作 Key 存入 Map,在 JS 中直接存是存引用的。
  • 通常需要将它们序列化成字符串。例如:['a', 'b'] -> "a,b" 作为 Key。

建议路线:先搞定 1 -> 242 -> 49,这三道通了,哈希表基本就入门了。然后挑战 560,这是很多大厂(如字节、美团)的分水岭题目。