LeetCode hot100:049 字母异位词分组:两种解法的深度解析
问题描述:
给你一个字符串数组,请你将字母异位词组合在一起。可以按任意顺序返回结果列表。(字母异位词是指由相同字母重排列形成的字符串)
示例1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]输出: [["bat"],["nat","tan"],["ate","eat","tea"]]解释:在 strs 中没有字符串可以通过重新排列来形成 "bat"。
字符串 "nat" 和 "tan" 是字母异位词,因为它们可以重新排列以形成彼此。
字符串 "ate" ,"eat" 和 "tea" 是字母异位词,因为它们可以重新排列以形成彼此。
示例2:
输入: strs = [""]输出: [[""]]
示例3:
输入: strs = ["a"]输出: [["a"]]
解决方法:
方法一:排序+哈希表
核心:将每个字符串排序,排序后的字符串作为字母异位词的键。
步骤:①创建哈希表,键为排序后的字符串,值为原字符串列表;
②遍历每个字符串,将其排序后作为键;
③将原字符串添加到对应键的列表中;
④返回哈希表的值。
示例:
处理第一个字符串s = "eat" groups = { "aet": ["eat"]} |
整体流程: 输入: ["eat", "tea", "tan", "ate", "nat", "bat"] 处理流程: 最终分组: |
适用性:字符串长度较小或中等。
方法二:计数+哈希表
核心:统计每个字符串中字母的出现次数,用计数数组作为字母异位词的键。
步骤:①创建哈希表,键为计数数组的元组形式,值为原字符串列表;
②遍历每个字符串,统计26个字母的出现次数;
③将计数数组转换为元组作为键;
④将原字符串添加到对应键的列表中作为值;
⑤返回哈希表的值
示例:对于“eat”来说
字符: e -> index = ord('e') - ord('a') = 4 count数组变化: |
适用性:大数据量,性能要求高。
算法详解:
方法一:
class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:from collections import defaultdict #导入defaultdict,用于创建默认值为列表的字典groups = defaultdict(list) #实例化一个defaultdict,当访问不存在的键时自动创建空列表作为值for s in strs:key = ''.join(sorted(s))groups[key].append(s)return list(groups.values()) #将字典中的值转换为列表返回
其中,sorted() 函数将字符串中的每个字符单独取出并排序,返回结果是按字母顺序排列的字符列表。而 ' ' .join() 则是将字符列表连接成一个字符串,空字符串 ' '表示字符之间不加任何分隔符。
复杂度分析:
- 时间复杂度:O(n × k) n 是字符串数量,k 是字符串平均长度,k logk是每个字符串排序。
- 空间复杂度:O(n × k) 存储所有字符串和哈希表。
方法二:
class Solution:def groupAnagrams(self, strs: List[str]) -> List[List[str]]:from collections import defaultdict #导入defaultdict,用于创建默认值为列表的字典groups = defaultdict(list) #实例化一个defaultdict,当访问不存在的键时自动创建空列表作为值for s in strs:count = [0] * 26 #创建长度为26的列表,统计26个小写字母出现次数 初始化全零for ch in s:index = ord(ch) - ord('a') #计算字符在count列表中的索引位置count[index] +=1key = tuple(count) #计数列表转换为元组,列表不可作为字典的键groups[key].append(s)return list(groups.values())
在Python语言中,字符不能直接进行算数运算,要使用 ord() 函数进行ASCII计算。而在C语言或C++中可以直接表示,Java中类型自动隐式转换。
复杂度分析:
- 时间复杂度:O(n × k) n 是字符串数量,k 是字符串平均长度 。
- 空间复杂度:O(n × k) 存储所有字符串和哈希表
总结:
- 哈希表是核心:两种方法都利用哈希表实现高效分组;
- 标识选择:方法一选择排序后的字符串,方法二选择计数数组;
- 性能方面:方法一代码更简洁,并且同样适用于字符串中包含大写字母的情况,方法二在大数据量时性能更好。
通过这个问题,更重要的是掌握利用统一标识进行分组的通用解题思路。