LeetCode 49:字母异位词分组(从“找规律”到 HashMap 建模)

1 阅读3分钟

这道题在 LeetCode 里不算难,但非常值得认真写一篇笔记
因为它真正考的不是 API,而是一句话:

你能不能找到“一组字符串的共同特征”,并把它当成 key?


一、什么是字母异位词?

先把概念说清楚。

如果两个字符串:

  • 含有的字符完全相同
  • 每个字符出现的次数也相同
  • 只是顺序不同

那么它们就是 字母异位词

例子:

"eat""tea""ate"  → 同一组
"tan""nat"        → 同一组

二、最关键的问题:怎么判断“是不是一组”?

这是整道题的灵魂。

我们需要一个统一的、可比较的特征,让:

  • 同一组的字符串 → 特征相同
  • 不同组的字符串 → 特征不同

一个非常自然的想法

把字符串里的字符排序

举个例子:

"eat" → ['e','a','t'] → 排序 → "aet"
"tea" → ['t','e','a'] → 排序 → "aet"

不管原来顺序如何,只要是字母异位词,
排序后的结果一定一样

这个排序后的字符串,就是我们要找的 key


三、整体思路(先看大框架)

  1. 遍历字符串数组

  2. 对每个字符串:

    • 转成字符数组
    • 排序
    • 作为 HashMap 的 key
  3. 相同 key 的字符串,放进同一个 List

  4. 最后取出 map 中所有 value 即可

一句话总结:

HashMap<String, List>,key 是“排序后的字符串”


四、代码实现

完整代码

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();

        for (String str : strs) {           // 1. 遍历字符串数组
            char[] chs = str.toCharArray(); // 2. 字符串 → 字符数组
            Arrays.sort(chs);               // 3. 排序
            String key = new String(chs);   // 4. 排序后的字符串作为 key

            if (map.containsKey(key)) {
                map.get(key).add(str);      // 5. 已存在:直接加入
            } else {
                List<String> list = new ArrayList<>();
                list.add(str);
                map.put(key, list);         // 6. 不存在:新建分组
            }
        }

        // 7. 直接取出所有分组
        return new ArrayList<>(map.values());
    }
}

五、为什么 HashMap 这么合适?

因为这道题本质是:

分类问题

而 HashMap 天生就是用来:

  • 用一个 key
  • 对应一组 value

这里:

  • key:排序后的字符串(分组依据)
  • value:所有属于这一组的原字符串

这比用双重循环去硬比较,清晰太多了。


六、一个容易被忽略的小细节

String key = new String(chs);

为什么不能直接用 char[] 当 key?

因为:

  • 数组在 Java 中是引用类型
  • 就算内容一样,地址不同,HashMap 也认为是不同 key

String

  • 是不可变对象
  • 重写了 equalshashCode
  • 非常适合作为 HashMap 的 key

七、时间 & 空间复杂度分析

设:

  • n = 字符串个数
  • k = 每个字符串的平均长度

时间复杂度

  • 每个字符串排序:O(k log k)
  • 总体:O(n * k log k)

空间复杂度

  • HashMap 存储所有字符串
  • 排序时使用字符数组
  • 总体:O(n * k)

八、这道题还能怎么优化?

如果字符集固定(比如只包含 a-z),可以用:

  • 字符频次数组
  • 再把频次数组编码成字符串当 key

时间复杂度可以降到 O(n * k)
但实现复杂度更高,不是这道题的重点。


九、小总结

这道题真正想教我们的不是排序,也不是 HashMap API,而是:

  • 如何从题目中 抽象“分组依据”
  • 如何把“相同特征”转化成 可比较的 key
  • 如何用 HashMap 把复杂问题结构化解决

很多字符串题,
本质都是:找 key → 分组 → 处理 value。