文章

哈希表专题:Mermaid 图解映射、计数去重与 4 道 LeetCode 核心题

用 Mermaid 图解哈希表中的查找、计数、去重、频次统计和 4 道 LeetCode 经典题,系统掌握哈希表专题。

哈希表专题:Mermaid 图解映射、计数去重与 4 道 LeetCode 核心题

哈希表是算法题里最容易被低估的一类结构。

很多题看起来像暴力、双层循环,真正的突破点其实只有一句话:

把“找过没有、出现几次、对应关系是什么”从 O(n) 查找变成 O(1) 级别查询。

这篇文章继续用 Mermaid 图解的方式,把哈希表最常见的映射、计数、去重和频次统计模型讲清楚,再用 4 道 LeetCode 题把高频套路串起来。

学习目标:

  1. 理解哈希表在算法题中的核心作用。
  2. 掌握映射、计数、去重、分组等高频模型。
  3. 理解什么时候应该优先想到哈希表。
  4. 用 4 道 LeetCode 题覆盖哈希表高频题型。
  5. 用一张知识卡片形成哈希表题的判断框架。

一、哈希表的本质:用空间换时间

哈希表的核心价值是:

把“查找某个元素是否存在”的代价,从线性扫描降到近似常数时间。

flowchart TD
    A[元素 / 键] --> B[哈希函数]
    B --> C[映射到表中的位置]
    C --> D[快速查询或更新]

这意味着它特别适合解决:

  • 是否存在
  • 出现次数
  • 值到位置的映射
  • 分类分组

二、哈希表高频模型

mindmap
  root((哈希表模型))
    映射
      数值到下标
      字符到计数
    计数
      频次统计
      次数比较
    去重
      判重
      集合交并
    分组
      同类归并

1. 映射

例如“两数之和”里,把数值映射到下标。

2. 计数

例如字符串字符频次、数组元素出现次数。

3. 去重

Set 判断元素是否已出现。

4. 分组

把某种“规范化后的 key”映射到一个集合。


三、什么时候该第一时间想到哈希表

如果题目里出现下面这些信号,哈希表通常都值得优先考虑:

  • 是否出现过
  • 是否重复
  • 出现次数
  • 配对关系
  • 分组归类
flowchart TD
    A[题目包含 查找 判重 计数 配对] --> B[优先考虑哈希表]

很多暴力题之所以慢,就是因为把“查找”写成了重复扫描。


四、4 道 LeetCode 题目打通哈希表专题

1)LeetCode 1. 两数之和

题型定位:值到下标映射。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> pos;
        for (int i = 0; i < static_cast<int>(nums.size()); ++i) {
            int need = target - nums[i];
            if (pos.count(need)) return {pos[need], i};
            pos[nums[i]] = i;
        }
        return {};
    }
};
flowchart TD
    A[遍历当前数 x] --> B[计算需要的数 target-x]
    B --> C{哈希表里有吗}
    C -->|有| D[直接得到答案]
    C -->|没有| E[把 x 和下标存入哈希表]

这题练的是:

  • 映射关系建模
  • 为什么能从 O(n^2) 降到 O(n)

2)LeetCode 49. 字母异位词分组

题型定位:哈希分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> groups;
        for (const string& s : strs) {
            string key = s;
            sort(key.begin(), key.end());
            groups[key].push_back(s);
        }
        vector<vector<string>> res;
        for (auto& [_, bucket] : groups) res.push_back(bucket);
        return res;
    }
};
flowchart TD
    A[原字符串] --> B[排序后得到标准 key]
    B --> C[相同 key 的字符串放进同一组]

这题训练的是:

  • 如何构造“等价类”的 key
  • 哈希表分组思路

3)LeetCode 128. 最长连续序列

题型定位:哈希集合判存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> st(nums.begin(), nums.end());
        int ans = 0;
        for (int num : st) {
            if (st.count(num - 1)) continue;
            int cur = num, len = 1;
            while (st.count(cur + 1)) {
                ++cur;
                ++len;
            }
            ans = max(ans, len);
        }
        return ans;
    }
};
flowchart TD
    A[当前数 num] --> B{num-1 存在吗}
    B -->|存在| C[它不是起点 跳过]
    B -->|不存在| D[从它开始向后扩展连续段]

这题训练的是:

  • 哈希集合判存在
  • 如何避免重复扩展

4)LeetCode 560. 和为 K 的子数组

题型定位:前缀和 + 哈希计数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> cnt;
        cnt[0] = 1;
        int sum = 0, ans = 0;
        for (int x : nums) {
            sum += x;
            if (cnt.count(sum - k)) ans += cnt[sum - k];
            ++cnt[sum];
        }
        return ans;
    }
};
flowchart TD
    A[当前前缀和 sum] --> B[查找之前是否出现过 sum-k]
    B --> C[出现次数就是新增答案数]
    C --> D[把当前 sum 计入哈希表]

这题最重要的是理解:

  • 哈希表有时不是直接存元素
  • 也可以存“某种中间状态”的出现次数

五、哈希表题怎么快速判断

flowchart TD
    A[看到题目] --> B{是否涉及 查找 判重 计数 配对}
    B -->|是| C[优先考虑 unordered_map / unordered_set]
    B -->|否| D{是否能把对象映射成规范 key}
    D -->|是| E[考虑哈希分组]
    D -->|否| F[再看双指针 排序 DP 等模型]

六、哈希表常见错误

1)key 设计不合理

分组类题里,key 设计是成败关键。

2)更新时机写错

例如前缀和题,先查还是先放入哈希表,顺序可能影响答案。

3)重复统计

尤其是在计数和组合类问题里。

4)只会用哈希表,不会结合别的结构

很多题其实是:

  • 哈希 + 前缀和
  • 哈希 + 滑动窗口
  • 哈希 + 排序
flowchart TD
    A[构造 key / 状态] --> B[查找是否存在或出现几次]
    B --> C[更新答案]
    C --> D[更新哈希表]

七、哈希表知识卡片

mindmap
  root((哈希表))
    核心作用
      快速查找
      用空间换时间
    高频模型
      映射
      计数
      去重
      分组
    组合用法
      前缀和
      滑动窗口
      排序
    常错点
      key 设计
      更新时机
      重复统计

复习版要点:

  • 哈希表最核心的价值是快速查找
  • 查找、判重、计数、配对题优先考虑哈希
  • 分组题往往关键在于构造规范 key
  • 哈希表经常和前缀和、滑动窗口联用
  • 更新顺序写错很容易导致答案偏差

八、最后总结

如果只记一句话,请记这个:

哈希表不是“一个容器”,而是一种把重复查找成本打掉的建模方式。

做题时先判断:

  • 我要不要快速判断存在性
  • 我是不是在统计频次
  • 我能不能把对象映射成统一 key

把这篇里的 4 道题做透,哈希表高频题会非常稳。

本文由作者按照 CC BY 4.0 进行授权