算法题解:字母异位词分组
问题描述
给定一个字符串数组 strs
,我们需要将其中所有字母异位词组合在一起。字母异位词是指由相同字母重新排列形成的不同单词。最终可以按任意顺序返回结果列表。
示例
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
提示
1 <= strs.length <= 10^4
0 <= strs[i].length <= 100
strs[i]
仅包含小写字母
解题思路
方法一:排序 + 哈希表
核心思想:字母异位词排序后的字符串是相同的,可以利用这一点作为哈希表的键来分组。
步骤:
- 排序字符串:对每个字符串进行排序,排序后的字符串作为哈希表的键。
- 分组存储:将原始字符串存储到对应键的列表中。
- 返回结果:哈希表中所有值组成的列表即为最终结果。
复杂度分析:
- 时间复杂度:O(nk log k),其中 n 是字符串数量,k 是字符串平均长度。排序每个字符串需要 O(k log k) 时间。
- 空间复杂度:O(nk),需要存储所有字符串。
方法二:计数 + 哈希表
核心思想:由于字符串仅包含小写字母,可以用一个长度为 26 的数组统计每个字母出现的次数,将计数数组转换为字符串作为哈希表的键。
步骤:
- 统计字母频率:对每个字符串统计各字母出现次数。
- 生成键:将计数数组转换为特定格式的字符串(如用
#
分隔的数字)作为键。 - 分组存储:将原始字符串存储到对应键的列表中。
- 返回结果:哈希表中所有值组成的列表即为最终结果。
复杂度分析:
- 时间复杂度:O(nk),统计字母频率需要 O(k) 时间。
- 空间复杂度:O(nk),需要存储所有字符串和计数键。
代码实现
方法一实现
from collections import defaultdict
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
groups = defaultdict(list)
for s in strs:
key = ''.join(sorted(s))
groups[key].append(s)
return list(groups.values())
代码解释:
- 初始化哈希表:使用
defaultdict
创建值为列表的字典。 - 排序字符串:对每个字符串排序后作为键。
- 分组存储:将原始字符串添加到对应键的列表中。
- 返回结果:将哈希表的所有值转换为列表返回。
方法二实现
from collections import defaultdict
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
groups = defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
key = '#'.join(map(str, count))
groups[key].append(s)
return list(groups.values())
代码解释:
- 初始化哈希表:同样使用
defaultdict
。 - 统计字母频率:创建长度为 26 的数组统计每个字母出现次数。
- 生成键:将计数数组转换为用
#
分隔的字符串作为键。 - 分组存储:将原始字符串添加到对应键的列表中。
- 返回结果:将哈希表的所有值转换为列表返回。
方法比较
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
排序+哈希表 | O(nk log k) | O(nk) | 字符串长度较小 |
计数+哈希表 | O(nk) | O(nk) | 字符串长度较大 |
总结
两种方法都能有效解决问题:
- 方法一实现更简单,适合字符串长度较小的情况。
- 方法二在理论上更高效,适合字符串长度较大的场景。
掌握这两种方法有助于应对不同约束条件下的类似问题。在实际应用中,可以根据具体需求选择最合适的方法。