力扣HOT100之:字母异位词分组

125 阅读3分钟

前言:这道题目是我第一道hot100,加油,冲冲冲!

题目解析

字母异位词(Anagram)是指由相同字母以不同顺序组成的单词。例如 "eat"、"tea"、"ate" 都包含字母 e、a、t,只是排列顺序不同。

题目要求我们将字符串数组中的所有字母异位词分组,每组包含所有互为异位词的字符串。

解题思路

核心思想:标准化键值

要判断两个字符串是否为字母异位词,最直接的方法是:

  1. 将两个字符串的字符按相同规则排序
  2. 如果排序后的结果相同,则它们是异位词

因此,我们可以将排序后的字符串作为哈希表的键,原始字符串作为值存储在对应的列表中。

算法步骤

  1. 创建一个哈希表,键为排序后的字符串,值为字符串列表

  2. 遍历输入数组中的每个字符串:

    • 对当前字符串进行排序,得到标准化的键
    • 将原始字符串添加到对应键的列表中
  3. 遍历哈希表,将所有值(字符串列表)收集到结果数组中

代码实现

cpp
编辑
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 使用哈希表存储分组结果
        // key: 排序后的字符串,value: 原始字符串列表
        unordered_map<string, vector<string>> anagramGroups;

        // 遍历每个字符串
        for (string s : strs) {
            string key = s;
            // 对字符串排序,得到标准化的键
            sort(key.begin(), key.end());
            // 将原始字符串添加到对应分组
            anagramGroups[key].push_back(s);
        }

        // 收集所有分组结果
        vector<vector<string>> result;
        for (auto& pair : anagramGroups) {
            result.push_back(pair.second);
        }

        return result;
    }
};

算法复杂度分析

时间复杂度:O(N × M log M)

  • N 是字符串数组的长度
  • M 是字符串的平均长度
  • 对每个字符串排序需要 O(M log M) 时间
  • 总共需要处理 N 个字符串

空间复杂度:O(N × M)

  • 哈希表需要存储所有字符串
  • 每个字符串在哈希表中存储一次

优化方案:计数法替代排序

对于只包含小写字母的字符串,我们可以使用字符计数来替代排序,进一步优化性能。

cpp
编辑
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> anagramGroups;
        
        for (string& s : strs) {
            // 创建字符计数数组
            vector<int> count(26, 0);
            for (char c : s) {
                count[c - 'a']++;
            }
            
            // 将计数数组转换为字符串作为键
            string key = "";
            for (int i = 0; i < 26; i++) {
                if (count[i] > 0) {
                    key += string(1, 'a' + i) + to_string(count[i]);
                }
            }
            
            anagramGroups[key].push_back(s);
        }
        
        vector<vector<string>> result;
        for (auto& pair : anagramGroups) {
            result.push_back(pair.second);
        }
        
        return result;
    }
};

计数法的时间复杂度:O(N × M)

  • 遍历每个字符串的每个字符:O(M)
  • 构建键值:O(26) = O(1)
  • 总体:O(N × M)

测试用例验证

示例 1

text
编辑
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
处理过程:
- "eat" → 排序 → "aet" → 分组["eat"]
- "tea" → 排序 → "aet" → 分组["eat", "tea"]
- "tan" → 排序 → "ant" → 分组["tan"]
- "ate" → 排序 → "aet" → 分组["eat", "tea", "ate"]
- "nat" → 排序 → "ant" → 分组["tan", "nat"]
- "bat" → 排序 → "abt" → 分组["bat"]

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

示例 2 & 3

  • 空字符串排序后仍为空字符串
  • 单字符字符串排序后不变

实际应用场景

  1. 文本处理:在文档分析中识别同义词或变体词
  2. 密码学:检测简单的字母替换密码
  3. 数据清洗:合并重复但拼写顺序不同的数据项
  4. 游戏开发:单词游戏中的字母组合验证

总结

这道题目完美展示了哈希表标准化处理的结合威力:

  1. 标准化是关键:通过排序将异位词映射到相同的键
  2. 哈希表高效分组:O(1) 的平均查找时间确保高效分组
  3. 灵活优化:根据输入特征选择排序或计数法

掌握这种"标准化 + 哈希分组"的思维模式,可以解决许多类似的分组问题,如按质因数分组、按数字和分组等。这是算法面试中的经典套路,值得深入理解和熟练掌握。一天6道力扣,大厂等着你!