LeetCode 49. 字母异位词分组 超详细题解(Python版)|排序法+字符计数法双解法
2026/6/10 20:10:48 网站建设 项目流程

一、题目原题重现

字母异位词分组是LeetCode哈希表应用的经典高频题,也是程序员面试必考算法题型,核心考察哈希表键值设计、字符串特征提取、分组逻辑实现,难度适中,非常适合巩固哈希表实战用法,夯实算法基础。

1.1 题目原文

给你一个字符串数组strs,请你将字母异位词组合在一起,可以按任意顺序返回结果列表。

字母异位词定义:由重新排列源单词的字母得到的新单词,且所有字母的出现次数完全相同(例如 “eat” 和 “tea” 互为字母异位词),两个单词可通过重排互相得到。

1.2 题目约束

  • 数组长度范围:1 <= strs.length <= 10⁴

  • 单个字符串长度:0 <= strs[i].length <= 100

  • 字符串仅包含小写英文字母

1.3 示例演示

示例演示示例 1 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"] 输出: [["bat"],["nat","tan"],["ate","eat","tea"]] 示例 2 输入: strs = [""] 输出: [[""]] 示例 3 输入: strs = ["a"] 输出: [["a"]]

二、解题思路全解析

2.1 核心问题拆解

解决字母异位词分组的核心关键:找到字母异位词的统一唯一特征标识,让同一组异位词拥有相同标识,不同组标识完全区分,再借助哈希表完成分组,这也是同类分组题型的通用解题逻辑。基于该核心,本题有两种主流解法,分别适配入门理解和最优答题场景。

2.2 解法一:排序法(直观基础解法)

核心思路

互为字母异位词的字符串,排序后得到的结果完全一致(例如 “eat” 和 “tea” 排序后均为 “aet”),利用这一特性,将排序后的字符串作为哈希表唯一键,实现分组存储。

  1. 初始化Python字典作为哈希表,键为排序后的字符串,值为对应字母异位词列表

  2. 遍历数组内每个字符串,对字符串排序并拼接,生成特征键

  3. 将原字符串追加到哈希表对应键的列表中,键不存在则初始化空列表

  4. 遍历完成后,将哈希表的值转为列表,即为最终分组结果

代码实现(Python)
解法一:排序法代码from typing import List class Solution: def groupAnagrams(self, strs: List[str]) -> List[List[str]]: # 哈希表:key=排序后字符串,value=对应字母异位词列表 anagram_map = {} for s in strs: # sorted返回字符列表,列表不可哈希,需用join拼接为字符串作为键 key = ''.join(sorted(s)) # 键不存在则初始化空列表,存在则直接追加当前字符串 if key not in anagram_map: anagram_map[key] = [] anagram_map[key].append(s) # 字典values为视图对象,强转列表后符合题目二维列表输出格式 return list(anagram_map.values()) # 本地测试验证 if __name__ == "__main__": sol = Solution() print(sol.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"])) print(sol.groupAnagrams([""])) print(sol.groupAnagrams(["a"]))
复杂度分析
  • 时间复杂度O ( n k log ⁡ k ) O(nk\log k)O(nklogk),其中n nn为字符串数组长度,k kk为单个字符串的最大长度;对每个字符串执行排序的耗时为O ( k log ⁡ k ) O(k\log k)O(klogk),共遍历n nn个字符串

  • 空间复杂度O ( n k ) O(nk)O(nk),额外空间主要用于哈希表存储所有字符串字符,不计返回结果本身的空间开销,属于必要空间消耗

优缺点
  • ✅ 优点:思路直观易懂,代码极简,无复杂逻辑,新手极易上手,适合理解核心分组逻辑

  • ❌ 缺点:排序操作有额外耗时,处理超长字符串时效率偏低

2.3 解法二:字符计数法(最优解,面试首选

核心思路

字母异位词的每个字符出现次数完全相同,利用这一本质特征,统计26个小写字母的出现次数,生成计数特征作为哈希表键,跳过排序步骤,时间复杂度优化至线性级别,是本题最优解法。

  1. 每个字符串对应初始化长度为26的计数列表,对应a-z,初始值全0,索引从0开始依次对应a到z

  2. 遍历字符串字符,通过ord()计算字符对应索引,统计每个字母出现次数

  3. Python中列表不可哈希,无法直接作为字典键,需转换为元组作为哈希表唯一特征键

  4. 按键分组存储,最后将哈希表值转为列表返回

代码实现(Python)
解法二:字符计数法(最优解)from typing import List class Solution: def groupAnagrams(self, strs: List[str]) -> List[List[str]]: # 哈希表:key=字符计数元组,value=字母异位词列表 anagram_map = {} for s in strs: # 初始化26位字母计数数组,索引0~25分别对应小写字母a~z count = [0] * 26 # 遍历字符,统计每个字母出现次数 for char in s: # 计算字符对应索引,从0开始计数,保证小写字母映射无偏差 idx = ord(char) - ord('a') count[idx] += 1 # 列表不可哈希,转为元组后作为字典键 key = tuple(count) # 键不存在则初始化,存在则追加 if key not in anagram_map: anagram_map[key] = [] anagram_map[key].append(s) return list(anagram_map.values()) # 进阶简化写法(可选,用defaultdict省去判空逻辑) # from collections import defaultdict # class Solution: # def groupAnagrams(self, strs: List[str]) -> List[List[str]]: # anagram_map = defaultdict(list) # for s in strs: # count = [0]*26 # for c in s: # count[ord(c)-ord('a')] +=1 # anagram_map[tuple(count)].append(s) # return list(anagram_map.values()) # 本地测试 if __name__ == "__main__": sol = Solution() print(sol.groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))
执行过程演示(以 “eat” 为例,索引从0开始计数)
  1. 初始化计数列表:[0,0,0,...,0](共26个0,对应a-z)

  2. 字符 ‘e’ 对应索引:ord('e')-ord('a')=4count[4] += 1[0,0,0,0,1,0,...0]

  3. 字符 ‘a’ 对应索引:ord('a')-ord('a')=0count[0] += 1[1,0,0,0,1,0,...0]

  4. 字符 ‘t’ 对应索引:ord('t')-ord('a')=19count[19] += 1[1,0,0,0,1,0,...,1,...0]

  5. 转换为元组作为键,“tea” 和 “ate” 会生成完全相同的键,自动归为一组

复杂度分析
  • 时间复杂度O ( n k ) O(nk)O(nk),仅需线性遍历数组所有字符,无排序等高耗时操作,遍历效率拉满

  • 空间复杂度O ( n k ) O(nk)O(nk),额外开销来自哈希表存储和固定大小的计数数组,不计返回结果空间,消耗合理

优缺点
  • ✅ 优点:时间最优,无排序开销,全场景适配,稳定性极强

  • ✅ 优点:特征键唯一性拉满,完全规避排序潜在问题,面试首选

  • ❌ 缺点:代码逻辑稍复杂,需理解字符编码与计数转换逻辑

三、核心注意事项与易错点

  • 空字符串处理:空字符串无字符,计数列表全0,排序后为空串,可正常单独分组,无需额外判断

  • 哈希键类型禁忌:Python列表不可作为字典键,计数列表必须转元组,排序后字符列表必须用join拼接为字符串,否则代码直接报错

  • 字符索引计算:通过ord(char)-ord('a')映射0-25索引,严格对应小写字母,无索引越界风险

  • 返回格式规范:字典values()为视图对象,必须用list()强转,才能得到题目要求的二维列表格式

四、两种解法性能对比

解法类型时间复杂度空间复杂度适用场景推荐指数
排序法O ( n k log ⁡ k ) O(nk\log k)O(nklogk)O ( n k ) O(nk)O(nk)字符串较短、追求代码简洁、快速入门⭐⭐⭐⭐
字符计数法O ( n k ) O(nk)O(nk)O ( n k ) O(nk)O(nk)全场景、面试答题、大规模数据处理⭐⭐⭐⭐⭐

五、全文总结与思考延伸

5.1 全文总结

  1. 本题核心考点哈希表键值设计、字符串特征提取、分组算法实现,是哈希表实战的经典入门题型;

  2. 排序法适合入门理解,代码简洁但时间复杂度偏高,适合小规模数据场景;

  3. 字符计数法是本题最优解,通过空间换时间,时间复杂度优化至O(nk),面试务必优先使用;

  4. 同类分组题型均可借鉴该思路:提取统一特征作为键,借助哈希表实现高效分组,通用性极强。

5.2 思考延伸

  • 通用场景适配:若字符串包含Unicode字符而非仅小写字母,排序法仍通用(直接按字符排序);计数法可改用collections.Counter统计字符频次,再转为元组作为键。

  • 代码进阶写法:Python中可使用defaultdict(list)省去键不存在的判空逻辑,简化代码,适合熟练后使用,基础学习阶段保留显式判断更易理解。

刷题延伸:吃透本题后,可练习 LeetCode 242.有效的字母异位词(基础前置题)、LeetCode 438.找到字符串中所有字母异位词(进阶题),巩固字符计数与哈希表用法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询