算法题:🔍哈希表的妙用——从“找最长连续数”到“字母异位词分组”

100 阅读6分钟

引言

在算法世界中,哈希表(Hash Table) 是一个非常强大又常用的工具。它不仅能实现快速查找,还能用于数据归类、去重、统计等任务。

今天,我们就通过两道经典的 LeetCode 题目:

  • LeetCode 128. 最长连续序列(Longest Consecutive Sequence)
  • LeetCode 49. 字母异位词分组(Group Anagrams)

来一起看看 哈希表的两种典型应用场景

哈希表

什么是哈希表?

哈希表(Hash Table)是一种用“键”来快速查找数据的数据结构。你可以把它想象成一个“超级快的字典”:你给它一个“关键词”(key),它就能立刻告诉你对应的“内容”(value)。

比如:

  • 你问:“‘apple’对应的中文是什么?”
  • 哈希表立刻回答:“‘苹果’。

哈希表的基本用法

哈希表最常用的两种形式是:

  • 字典(map) :存储键值对,比如 { "name": "小明" }
  • 集合(set) :存储唯一值,比如 {1, 2, 3}

在不同的编程语言中,哈希表可能有不同的名称:

  • 在 Python 中,它叫 dict(字典)
  • 在 C++  中,它叫 unordered_map
  • 在 Java 中,它叫 HashMap

无论叫什么名字,它们的核心功能都差不多:快速查找、插入和删除

 哈希表的三大特点

  1. 查找特别快
    不管里面有多少数据,查找一个值是否存在,或者查找某个 key 对应的 value,几乎都是“瞬间完成”的。

  2. 插入和删除也快
    添加或删除一个数据也非常高效。

  3. 可以用来去重、归类、统计

  • 用集合(set)去掉重复的数字
  • 用字典(map)统计每个单词出现的次数
  • 把相同特征的数据归类在一起(比如字母异位词分组

常见操作及方法

1. 插入数据

插入数据就是给哈希表添加一个新的键值对

unordered_map<string, string> myMap;
myMap["apple"] = "苹果";
2. 查找数据

查找数据是通过键来获取对应的值。

if (myMap.find("apple") != myMap.end()) {
    cout << myMap["apple"] << endl;  // 输出: 苹果
}
3. 删除数据

删除数据是根据键来移除相应的键值对。

myMap.erase("apple");
4. 遍历哈希表

遍历哈希表可以查看所有的键值对。

for (auto& pair : myMap) {
    cout << pair.first << ": " << pair.second << endl;
}
5. 检查键是否存在

检查某个键是否存在于哈希表中。

if (myMap.find("apple") != myMap.end()) {
    cout << "存在" << endl;
}
6. 获取所有键或值

有时候我们需要获取哈希表中的所有键或所有值。

vector<string> keys;
vector<string> values;
for (auto& pair : myMap) {
    keys.push_back(pair.first); // 键
    values.push_back(pair.second); // 值
}

 小结一句话:

哈希表就像一本查得特别快的字典,能让你在一堆数据中快速找到你要的东西,或者把东西按规则归类。掌握几个基本操作——插入、查找、删除、遍历,就能应对大多数日常需求了。

题目解析

一、找最长连续序列:哈希集合 + 智能判断起点

🧩 问题描述

给定一个未排序的整数数组 nums,找出最长的连续数字序列的长度。例如:

输入:[100, 4, 200, 1, 3, 2]
输出:4
解释:最长连续序列是 [1, 2, 3, 4]

传统的做法可能是先排序,再遍历找出最长连续序列。但排序的时间复杂度是 O(n log n),不是我们想要的。我们希望找到一个线性时间 O(n) 的解法。

这时候,哈希集合就派上用场了。

我们首先将所有数字放入一个 unordered_set 中,这样查找一个数字是否存在的操作只需要 O(1) 的时间。然后我们遍历集合中的每一个数字,只有当当前数字的前一个数字不存在时,才开始向上查找连续序列

比如,如果当前数字是 3,而 2 不在集合中,那 3 就可能是一个连续序列的起点。

我们从这个起点开始,不断查找 current_num + 1 是否存在,直到找不到为止,同时记录当前序列的长度。

在整个过程中,我们只对每个数字处理一次,因此时间复杂度是 O(n),空间复杂度也是 O(n)。

这个思路的关键在于:我们只从真正的起点开始查找,避免了重复遍历。而哈希集合的快速查找能力,让我们可以轻松判断一个数字是否存在。

核心代码(C++):

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        int length = 0;
        unordered_set<int> numSet;

        for(int i : nums){
            numSet.insert(i);// 用set集合存储
        }
        for(int num : numSet){
            if(numSet.find(num - 1) == numSet.end()){
                int currentNum = num;
                int currentLength = 1;

                while(numSet.find(currentNum + 1) != numSet.end()){
                    currentNum++;
                    currentLength++;
                }

                //更新最长长度
                length = max(length,currentLength);
            }
        }
        return length;
    }
};

哈希的妙用

在这个问题中,我们用哈希集合来:

  • 快速查找一个数字是否存在
  • 避免重复查找连续序列
  • 实现 O(n) 时间复杂度

二、字母异位词分组:哈希映射 + 智能归类

🧩 问题描述

给定一个字符串数组,将字母异位词(即字母相同但顺序不同的字符串)归类到一起。例如:

输入:["eat", "tea", "tan", "ate", "nat", "bat"]
输出:[["bat"],["nat","tan"],["ate","eat","tea"]]

这道题的核心在于,如何判断两个字符串是否是字母异位词。一个很聪明的办法是:将每个字符串排序后作为 key

因为字母异位词排序后会得到完全相同的字符串。

于是,我们可以使用一个 unordered_map<string, vector<string>>,其中 key 是排序后的字符串,value 是原始字符串的列表。我们对每个字符串进行排序,生成 key,然后将原始字符串插入到对应的 value 中。

最后,我们只需要遍历整个哈希表,把所有的 value 提取出来,就能得到最终结果。

这个思路的巧妙之处在于,我们用排序后的字符串作为统一的“指纹” ,然后利用哈希表自动归类的功能,把所有相同特征的字符串归为一组。

核心代码(C++):

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> map;
        for(string& s : strs){
           string key = s;
           sort(key.begin(),key.end());// 按ASCII码从小到大排序作为key
           map[key].push_back(s);// 相同key会归类到一起
        }
        vector<vector<string>> result;
        for(auto& str : map){
            result.push_back(str.second);// 把归类好的value给push到result
        }
        return result;
    }
};

哈希的妙用

在这个问题中,我们用哈希映射来:

  • 自动归类具有相同字母的字符串
  • 利用排序后的字符串作为统一的 key

总结:哈希的两种经典用法

问题哈希结构用途核心技巧
最长连续序列unordered_set快速查找判断是否是连续序列起点
字母异位词分组unordered_map<string, vector<string>>智能归类用排序字符串作为 key

通过这两道题,我们可以看到,哈希思想在算法中的应用非常广泛。它不仅帮助我们快速查找,还能帮我们高效归类、去重、统计,甚至解决一些看似复杂的问题。

无论你是刚开始学习算法的新手,还是正在准备面试的进阶者,掌握哈希思想,都将成为你解决问题的重要武器。