力扣解题-字母异位词分组
题目: 给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 指字母相同,但排列不同的字符串。
示例 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"]]
提示: 1 <= strs.length <= 104 0 <= strs[i].length <= 100 strs[i] 仅包含小写字母
Related Topics 数组 哈希表 字符串 排序
第一次解答
解题思路
核心方法:排序生成唯一标识 + 哈希表分组存储,利用字母异位词排序后结果完全相同的特性,将排序后的字符串作为哈希表的唯一key,实现异位词的精准分组,逻辑直观且效率优异。
具体步骤:
- 初始化一个
HashMap,键为排序后的字符串(作为异位词的唯一标识),值为对应异位词组成的字符串列表,用于存储分组结果。 - 遍历输入的字符串数组
strs,对每个字符串执行独立的分组处理: a. 将当前字符串转换为字符数组,通过Arrays.sort()对字符数组进行排序(异位词排序后会得到完全相同的字符数组)。 b. 将排序后的字符数组重新转为字符串sortedStr,该字符串即为当前字符串所属异位词组的唯一key。 c. 校验哈希表中是否存在该key: - 若存在,直接将原字符串添加到该key对应的列表中; - 若不存在,新建一个字符串列表,将原字符串加入列表后,把key-列表键值对存入哈希表。 - 遍历完成后,初始化一个结果列表
result,遍历哈希表的所有key,将每个key对应的异位词列表依次加入结果列表。 - 返回最终的分组结果列表
result。
执行耗时:7 ms,击败了97.34% 的Java用户 内存消耗:49.1 MB,击败了14.72% 的Java用户
public List<List<String>> groupAnagrams(String[] strs) {
Map<String,List<String>> map=new HashMap<>();
for(int i=0;i<strs.length;i++){
char[] chars = strs[i].toCharArray();
Arrays.sort(chars);
String sortedStr = new String(chars);
if(map.containsKey(sortedStr)){
map.get(sortedStr).add(strs[i]);
}else{
List<String> list=new ArrayList<>();
list.add(strs[i]);
map.put(sortedStr,list);
}
}
List<List<String>> result=new ArrayList<>();
for(String key:map.keySet()){
result.add(map.get(key));
}
return result;
}
leetcode经典解答
解题思路
核心方法:延续排序标识分组核心逻辑 + Java8流式编程简化实现,与第一次解答的核心算法完全一致,通过Java8的流式API和集合收集器替代手动的for循环与哈希表操作,大幅精简代码行数,让代码更优雅、更简洁。
具体步骤:
- 将字符串数组
strs通过Arrays.stream()转换为流式数据,开启流式处理; - 调用流的
collect()方法,使用Collectors.groupingBy()收集器完成自动分组: a.groupingBy()的核心入参为分组规则函数:对每个流中的字符串,先转字符数组排序,再转回字符串,以此作为分组的唯一key(与第一次解答的key生成逻辑完全一致); b.groupingBy()会自动创建哈希表,按上述key将所有字符串分组,最终返回一个Map<String, List<String>>,与第一次解答手动构建的哈希表完全相同。 - 直接获取分组后哈希表的
values()集合(所有异位词分组列表),将其作为入参新建ArrayList,完成结果列表的封装; - 直接返回该结果列表,无需额外的遍历与赋值操作。
核心优化点说明:
- 完全复用排序生成唯一标识的核心逻辑,算法效率与第一次解答基本一致;
- 用流式编程替代手动遍历数组和手动遍历哈希表,消除冗余的循环与判断代码,代码更简洁、可读性更强;
- 借助
groupingBy收集器自动完成哈希表的创建、key判断与列表存储,替代手动的containsKey/put/get操作,减少代码书写量。
public List<List<String>> groupAnagrams(String[] strs) {
return new ArrayList<>(Arrays.stream(strs)
.collect(Collectors.groupingBy(str -> {
// 返回 str 排序后的结果。
// 按排序后的结果来grouping by,算子类似于 sql 里的 group by。
char[] array = str.toCharArray();
Arrays.sort(array);
return new String(array);
})).values());
}
总结
- 两次解答的核心算法完全一致,均基于「字母异位词排序后结果相同」的特性,以排序后的字符串为唯一标识,通过哈希表实现分组,时间效率优异。
- 第一次解答是基础手动实现,通过显式循环和哈希表操作完成分组,逻辑步骤清晰,适合新手理解核心思路;
- LeetCode经典解答是Java8流式编程优化实现,复用核心逻辑的同时,通过流式API和收集器简化代码,更符合现代Java的编码风格,代码更简洁优雅。
- 两次解法的性能基本持平,经典解答无额外性能开销,仅为代码写法的优化。