跳到主要内容

49. 字母异位词分组

题目描述

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

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例

示例 1:

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

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

解题思路

第一步:理解问题本质

字母异位词的特点是:排序后的字符串相同

例如:

  • "eat", "tea", "ate" 排序后都是 "aet"
  • "tan", "nat" 排序后都是 "ant"

第二步:暴力解法

思路:对每个字符串,检查其他字符串是否是它的异位词。

class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
n = len(strs)
used = [False] * n
result = []

for i in range(n):
if used[i]:
continue
group = [strs[i]]
used[i] = True
for j in range(i + 1, n):
if not used[j] and sorted(strs[i]) == sorted(strs[j]):
group.append(strs[j])
used[j] = True
result.append(group)

return result

缺点:时间复杂度 O(n² × k log k),效率太低。

第三步:最优解法 —— 哈希表

核心洞察

  • 异位词排序后的结果相同,可以作为 key
  • 使用哈希表将相同 key 的字符串分到同一组

算法步骤

  1. 创建一个空哈希表
  2. 遍历每个字符串:
    • 将字符串排序作为 key
    • 将原字符串加入 key 对应的列表
  3. 返回哈希表的所有值

为什么正确

  • 异位词排序后必然相同,所以会被分到同一组
  • 非异位词排序后不同,会被分到不同组

完整代码实现

from typing import List

class Solution:
"""
字母异位词分组 - 哈希表

核心思想:
字母异位词(anagram)的特点是:排序后的字符串相同。
例如 "eat", "tea", "ate" 排序后都是 "aet"。

算法步骤:
1. 遍历每个字符串,将其排序作为 key
2. 将原字符串加入 key 对应的列表
3. 返回所有列表

为什么用排序作为 key?
因为异位词的字母组成完全相同,排序后必然相同。

时间复杂度:O(n * k log k),n 是字符串数量,k 是最大字符串长度
空间复杂度:O(n * k)
"""

def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
anagrams = {}
for s in strs:
key = ''.join(sorted(s)) # 排序作为 key
if key not in anagrams:
anagrams[key] = []
anagrams[key].append(s)
return list(anagrams.values())

示例推演

strs = ["eat","tea","tan","ate","nat","bat"] 为例:

字符串排序后哈希表状态
"eat""aet"{"aet": ["eat"]}
"tea""aet"{"aet": ["eat", "tea"]}
"tan""ant"{"aet": ["eat", "tea"], "ant": ["tan"]}
"ate""aet"{"aet": ["eat", "tea", "ate"], "ant": ["tan"]}
"nat""ant"{"aet": [...], "ant": ["tan", "nat"]}
"bat""abt"{"aet": [...], "ant": [...], "abt": ["bat"]}

结果:[ ["eat","tea","ate"], ["tan","nat"], ["bat"] ]


复杂度分析

解法时间复杂度空间复杂度说明
暴力O(n² × k log k)O(n × k)两两比较
哈希表(最优)O(n × k log k)O(n × k)n是字符串数量,k是最大长度

易错点总结

1. key 的生成

key = ''.join(sorted(s))  # 正确
key = sorted(s) # 错误,返回的是列表,不能作为字典 key

2. 空字符串的处理

空字符串排序后仍是空字符串,可以正常处理。

3. 使用 defaultdict 简化代码

from collections import defaultdict

anagrams = defaultdict(list)
for s in strs:
anagrams[''.join(sorted(s))].append(s)
return list(anagrams.values())

扩展思考

1. 如果不使用排序?

可以用字符计数作为 key。例如 "eat" 的计数是 a:1, e:1, t:1,可以表示为字符串 "1#1#1#0#..." 或元组 (1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0)

时间复杂度可以优化到 O(n × k)。

2. 如果需要返回每组内排序后的结果?

可以在返回前对每个组进行排序。


相关题目

加载评论中...