avascript
编辑
var groupAnagrams = function(strs) {
const map = new Map();
for (let i = 0; i < strs.length; i++) {
let key = strs[i].split('').sort().join('');
if (map.has(key)) {
map.get(key).push(strs[i]);
} else {
map.set(key, [strs[i]]);
}
}
return Array.from(map.values());
};
其中最关键的一步是:
js
编辑
let key = strs[i].split('').sort().join('');
以及最后的:
js
编辑
return Array.from(map.values());
本文将逐层拆解这行“魔法代码”背后的 API 原理,并解释 map.values() 到底返回了什么。
一、split(''):字符串 → 字符数组
作用
将字符串按指定分隔符拆分为数组。当分隔符为 ''(空字符串)时,会逐字符拆分。
语法
js
编辑
str.split(separator)
示例
js
编辑
"eat".split('') // → ["e", "a", "t"]
"hello".split('') // → ["h", "e", "l", "l", "o"]
"".split('') // → []
注意事项
separator可以是字符串或正则表达式;- 拆分不会修改原字符串(字符串不可变);
- 空字符串拆分会得到空数组。
✅ 为什么用
''?
因为我们需要把单词打散成单个字母,才能进行后续排序。
二、.sort():对字符数组排序(原地修改)
作用
对数组元素进行排序,默认按字符串的 Unicode 编码升序排列。
语法
js
编辑
arr.sort([compareFunction])
示例
js
编辑
["e", "a", "t"].sort() // → ["a", "e", "t"]
["z", "b", "a"].sort() // → ["a", "b", "z"]
关键细节
- 默认排序规则:将所有元素转为字符串后比较 Unicode 值;
- 原地修改:直接改变原数组,不返回新数组;
- 对于字母异位词(anagram),如
"eat"和"tea",排序后结果完全一致。
对比陷阱(数字排序)
js
编辑
[10, 2, 3].sort() // → [10, 2, 3] ❌(按字符串 "10" < "2")
[10, 2, 3].sort((a,b)=>a-b) // → [2, 3, 10] ✅
幸运的是,在本题中我们只处理字母,无需担心数字问题。
三、.join(''):字符数组 → 排序后的字符串
作用
将数组元素拼接成字符串,使用指定分隔符连接。
语法
js
编辑
arr.join(separator)
示例
js
编辑
["a", "e", "t"].join('') // → "aet"
["a", "b", "c"].join('-') // → "a-b-c"
[].join('') // → ""
注意事项
- 默认分隔符是
,; - 传入
''表示无间隔拼接; - 返回新字符串,不修改原数组。
四、完整流程演示(以 "eat" 为例)
js
编辑
"eat"
.split('') // → ["e", "a", "t"]
.sort() // → ["a", "e", "t"](原数组被修改)
.join('') // → "aet"
🔑 核心思想:
所有字母异位词经过此流程后,会生成相同的 key。
例如:
"eat"→"aet""tea"→"aet""ate"→"aet"
因此,我们可以用这个 key 作为 Map 的键,实现自动分组。
五、map.values():获取所有分组结果
返回值类型
Map.prototype.values() 返回一个 Map Iterator(迭代器对象) ,包含 Map 中所有 value(按插入顺序)。
示例
js
编辑
const map = new Map();
map.set("aet", ["eat", "tea"]);
map.set("abt", ["bat"]);
const iter = map.values();
console.log(iter); // MapIterator { ["eat", "tea"], ["bat"] }
如何转为数组?
由于迭代器不能直接用于返回(LeetCode 要求数组),需转换:
js
编辑
// 方法1:Array.from()
Array.from(map.values())
// 方法2:扩展运算符
[...map.values()]
两者效果相同,最终返回:
js
编辑
[ ["eat", "tea"], ["bat"] ]
迭代器特性
| 特性 | 说明 |
|---|---|
| 一次性 | 遍历完后无法再次使用(需重新调用 .values()) |
| 有序 | 严格按插入顺序返回 |
| 不实时同步 | 遍历时修改 Map,迭代器内容可能不更新 |
六、总结要点 ✅
| 步骤 | API | 作用 | 是否修改原数据 |
|---|---|---|---|
| 1 | split('') | 字符串 → 字符数组 | 否 |
| 2 | sort() | 字符数组排序 | 是(原地) |
| 3 | join('') | 数组 → 排序字符串(key) | 否 |
| 4 | map.values() | 获取所有分组 | 否(返回迭代器) |
| 5 | Array.from() | 迭代器 → 数组 | 否 |
💡 为什么这个组合如此经典?
它利用“排序归一化”思想,将语义相同但顺序不同的数据映射到同一标识符,是哈希分组的典型范式。