49. 字母异位词分组

49. 字母异位词分组

✨核心逻辑

本题采用 哈希表(HashMap)与特征编码 的策略:

  1. 统一特征键:字母异位词的特点是它们包含的字母种类和数量完全一致,只是排列顺序不同。因此,我们可以为每个字符串生成一个基于字母出现次数的唯一特征键
  2. 统计字符频率:遍历字符串的每一个字符,使用一个长度为 26 的整数数组 arr_char 统计每个字母出现的频次。
  3. 生成 Key:将统计数组拼接成一个具有独特标识的字符串(例如使用 #1#0#0... 等形式,防止数字拼接到一起产生歧义),作为哈希表的 Key
  4. 分组归类:遍历输入数组 strs,如果生成的 Keymap 中不存在,则创建一个新的列表并将当前字符串存入,然后将 Key 和该列表放入 map;如果 Key 已存在,直接将当前字符串追加到对应的列表中。
  5. 输出结果:遍历结束后,返回 map 中所有的 value 列表集合。

🔥代码实现(含详细变量注释)

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        // map:哈希表,用于存储 字母异位词的特征键 与 对应的字符串列表 的映射关系
        Map<String, List<String>> map = new HashMap<>();

        // 遍历外层的字符串数组,处理每一个字符串
        for (String str : strs) {
            // arr_char:长度为 26 的数组,用于统计当前字符串中每个字母(a-z)的出现次数
            int[] arr_char = new int[26];
            // 遍历当前字符串的每一个字符,更新计数数组
            for (char ch : str.toCharArray()) {
                arr_char[ch - 'a']++;
            }
            
            // sb:用于将统计数组转换拼接成一个唯一的字符串特征键(Key)
            StringBuilder sb = new StringBuilder();
            for (int num : arr_char) {
                sb.append("#"); // 使用 '#' 作为分隔符,防止数字拼接时发生混淆(例如 1 和 12 变成 112)
                sb.append(num);
            }
            // 将 StringBuilder 转换为不可变的 String,作为哈希表的 Key
            String key = sb.toString();
            
            // 判断特征键是否已存在于哈希表中
            if (map.containsKey(key)) {
                // 如果存在,直接将当前字符串添加到对应的列表中
                map.get(key).add(str);
            } else {
                // 如果不存在,创建一个新的列表,把当前字符串放进去
                List<String> list = new ArrayList<>();
                list.add(str);
                // 将新的键值对放入哈希表中
                map.put(key, list);
            }
        }
        // 返回哈希表所有的 values(即分组好的列表集合)
        return new ArrayList<>(map.values());
    }
}

  • ⏱️复杂度分析
    • 时间复杂度:O(N * K),其中 N 是字符串数组 strs 的长度,K 是数组中字符串的最大长度。外层需要遍历 N 个字符串,内部需要遍历字符统计 O(K) 以及拼接固定 26 个长度的数组 O(26)(常数级)。总体近似为 O(N * K)。

    • 空间复杂度:O(N * K),其中 N 是字符串的数量,K 是字符串的最大长度。最坏情况下,哈希表 map 需要存储所有 N 个字符串,以及它们对应的特征键 key。

🔍总结一下:

换个角度思考问题:这只是字母顺序不同,组成是相同的,也就意味着开个26个英文字母的计数数组存储字母出现的数量,相同的就是异位词

这只是一个数组,如何根据它判断和别的单词是同一组的?,存到Map<String, List《String》>里面,用上述计数数组生成一个key,判断key是否存在即可