小哆啦解题记——异位词界的社交网络

📅 2025-07-08|Day 35

题号:49. 字母异位词分组 - 力扣(LeetCode)

关键词:排序、哈希、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梦点头:

"这道题,不只是字符串的分组问题,

更是哈希思想的艺术展现。"

相关推荐
你的冰西瓜31 分钟前
C++ 中最短路算法的详细介绍
c++·算法·图论·最短路
zstar-_37 分钟前
【算法笔记】6.LeetCode-Hot100-链表专项
笔记·算法·leetcode
Swift社区42 分钟前
Swift 图论实战:DFS 算法解锁 LeetCode 323 连通分量个数
算法·swift·图论
<但凡.1 小时前
数据结构与算法之美:广义表
数据结构·c++·算法
前端极客探险家1 小时前
告别卡顿与慢响应!现代 Web 应用性能优化:从前端渲染到后端算法的全面提速指南
前端·算法·性能优化
程序员Xu2 小时前
【OD机试题解法笔记】连续出牌数量
笔记·算法·深度优先
CoovallyAIHub2 小时前
单目深度估计重大突破:无需标签,精度超越 SOTA!西湖大学团队提出多教师蒸馏新方案
深度学习·算法·计算机视觉
CoovallyAIHub2 小时前
从FCOS3D到PGD:看深度估计如何快速搭建你的3D检测项目
深度学习·算法·计算机视觉
偷偷的卷2 小时前
【算法笔记 day three】滑动窗口(其他类型)
数据结构·笔记·python·学习·算法·leetcode
北京地铁1号线2 小时前
Zero-Shot(零样本学习),One-Shot(单样本学习),Few-Shot(少样本学习)概述
人工智能·算法·大模型