📅 2025-07-08|Day 35
关键词:排序、哈希、Map、分组思想、分桶模型
第一章:字符串的社交圈风波
"哆啦A梦!这些单词在搞小团体!吃饭都不带我!"
大雄气鼓鼓地指着纸上的一串单词:
css
["eat", "tea", "tan", "ate", "nat", "bat"]
"他们看起来毫无关系,但好像背地里偷偷分组了。"
哆啦A梦一边吃铜锣烧一边笑道:
"这些是字母异位词社群,看似毫无关联,其实是重组基因的同胞兄弟。"
第二章:字母异位词社交法则
所谓字母异位词,就是:一群字符长得一样,只是顺序打乱了。
就像穿校服的一群人------换了发型但内核一致。
于是,大雄写下了解析器,准备"识别每个词的DNA":
ini
function groupAnagrams(strs: string[]): string[][] {
let sArr: string[] = [] // 存储每个词的"基因序列"
let result: string[][] = [] // 最终的朋友圈分组
let sMap: Map<string, number> = new Map(); // key: DNA,value: 朋友圈编号
for (let i = 0; i < strs.length; i++) {
sArr[i] = strs[i].split("").sort().join(""); // 基因排序
}
for (let i = 0, j = 0; i < strs.length; i++) {
if (sMap.has(sArr[i])) {
result[sMap.get(sArr[i])!].push(strs[i]);
} else {
result[j] = [strs[i]];
sMap.set(sArr[i], j++);
}
}
return result;
}
✅ 利用排序后的字符串作为"身份ID",将所有异位词塞进同一个数组。
第三章:分桶模型 · 字母排序做哈希键!
哆啦A梦看了看代码,点点头:
"你这方法已经很不错了!关键思路就是把 '词'映射成统一的 key,像是基因分类分桶。"
-
对每个字符串排序,统一成 key,如:
- "eat" → "aet"
- "tea" → "aet"
- "tan" → "ant"
-
然后用 Map 把这些 key 挂钩分组。
第四章:简洁优化·直接映射收集器!
哆啦A梦擦了擦嘴角的铜锣烧屑:"你其实不用 sArr[]
也能完成,只要边遍历边插入 map 就行。"
优化版:
vbnet
function groupAnagrams(strs: string[]): string[][] {
const map: Map<string, string[]> = new Map();
for (let str of strs) {
const key = str.split('').sort().join('');
if (!map.has(key)) {
map.set(key, []);
}
map.get(key)!.push(str);
}
return Array.from(map.values());
}
✅ 精简写法,省去多余数组,逻辑更直接。
第五章:如果我不用排序?有更快的哈希吗?
大雄疑惑:"那排序岂不是 O(n log k),有没有更快的键?"
哆啦A梦眯眼一笑:
"我们可以不用排序,而是直接统计字母频次,拼成 key!"
例子:"ate"
→ a:1, t:1, e:1
→ "1a1e1t"
(作为 key)
vbnet
function groupAnagrams(strs: string[]): string[][] {
const map: Map<string, string[]> = new Map();
for (let str of strs) {
const count = new Array(26).fill(0);
for (let char of str) {
count[char.charCodeAt(0) - 97]++;
}
const key = count.join('#');
if (!map.has(key)) map.set(key, []);
map.get(key)!.push(str);
}
return Array.from(map.values());
}
🧠 用字母频率拼接出的 key 可避免排序,适合大数据时优化性能。
第六章:测试场开放!
css
groupAnagrams(["eat", "tea", "tan", "ate", "nat", "bat"]);
// 输出可能是:[["bat"],["nat","tan"],["ate","eat","tea"]]
groupAnagrams([""]);
// [[""]]
groupAnagrams(["a"]);
// [["a"]]
📌 技术启示录:
解法 | Key类型 | 时间复杂度 | 空间复杂度 | 特点 |
---|---|---|---|---|
排序法 | 排序后的字符串 | O(nk log k) | O(nk) | 简单清晰、容易实现 |
字母频率法 | 频次拼接字符串 | O(nk) | O(nk) | 更快但 key 构造复杂 |
Map分组思想 | 哈希 + 动态数组 | 高效 | 灵活扩展 | 适合各种结构化聚类问题 |
✅ 其中
n
是字符串个数,k
是每个字符串的平均长度。
🌌 终章:字符串的社交网络
大雄若有所思地写下总结:
"每一个字符串,都在等待它的同类。
哪怕它们被打乱、重排,只要内核一样,终将重逢。"
哆啦A梦点头:
"这道题,不只是字符串的分组问题,
更是哈希思想的艺术展现。"