# JavaScript 算法详解:Hash表在字母异位词分组的应用(小白也能看懂)

3 阅读4分钟

JavaScript 算法详解:Hash表在字母异位词分组的应用(小白也能看懂)

本文将从零基础开始,用最通俗的语言详细解释 JavaScript 中「字母异位词分组」算法,就像老师在课堂上讲解一样。

什么是「字母异位词分组」?

首先我们来看一个生活中的例子:

  • "listen"(听) 和 "silent"(安静) 这两个单词,它们的字母都是一样的,只是顺序不同 → 这就是字母异位词
  • "eat""tea" 也是一样的道理

现在的问题是:给你一堆单词,把所有字母异位词分到一组

比如给你:["eat", "tea", "tan", "ate", "nat", "bat"]

应该分成:

  • 第一组:["eat", "tea", "ate"] (它们都是由 a,e,t 组成的)
  • 第二组:["tan", "nat"] (它们都是由 a,n,t 组成的)
  • 第三组:["bat"] (只有它一个)

JavaScript 代码详细解释

var groupAnagrams = function(strs) {
  const m = new Map(); // 1. 创建一个"分类盒子"

  for (const s of strs) { // 2. 挨个看每个单词
    // 3. 把单词的字母重新排列(排序)
    const sortedS = s.split('').sort().join('');

    if (!m.has(sortedS)) { // 4. 检查有没有这种"字母组合"的盒子
      m.set(sortedS, []); // 5. 如果没有,就新建一个空盒子
    }

    // 6. 把单词放进对应的盒子里
    m.get(sortedS).push(s);
  }

  // 7. 把所有的盒子拿出来,组成一个大包裹返回
  return Array.from(m.values());
};

一步步详细拆解

1. var groupAnagrams = function(strs) {

var groupAnagrams = function(strs) {
  • groupAnagrams函数的名字(就像一个机器的名字叫"分词机")
  • strs输入参数(就像你把一堆单词扔进机器)
  • function 是告诉电脑:"我定义一个函数,名字叫 groupAnagrams,它会接收一个叫 strs 的参数"

比如:groupAnagrams(["eat", "tea"]) → 你把 ["eat", "tea"] 传给函数

2. const m = new Map();

const m = new Map();
  • const:声明一个常量(不能被修改的变量),就像一个标签
  • m:这是给这个东西起的名字,就像"分类柜"
  • new Map():创建一个"哈希表"(可以想象成一个有很多抽屉的柜子)

Map 就像一个智能分类柜

  • 你可以给它一个"钥匙"(key),它就能给你对应的"物品"(value)
  • 比如:柜子.放东西("钥匙A", "苹果") → 把苹果放在钥匙A对应的抽屉里
  • 柜子.拿东西("钥匙A") → 拿到苹果

3. for (const s of strs) {

for (const s of strs) {
  • for:表示"循环"(重复做某件事)
  • const s:每次循环时,用 s 来代表当前的单词
  • of strs:在 strs 数组中一个一个地取单词

如果 strs = ["eat", "tea", "tan"]

  • 第1次循环:s = "eat"
  • 第2次循环:s = "tea"
  • 第3次循环:s = "tan"

4. const sortedS = s.split('').sort().join('');

这是最神奇的一步!我们来拆开看:

4.1 s.split('')
"eat".split('') → ["e", "a", "t"]
  • split(''):把字符串拆成一个个字母组成的数组
4.2 .sort()
["e", "a", "t"].sort() → ["a", "e", "t"]
  • sort():把数组里的元素按字母顺序排列
4.3 .join('')
["a", "e", "t"].join('') → "aet"
  • join(''):把数组里的元素拼接成一个字符串

所以:

  • "eat""aet"
  • "tea""aet" ← 它们排序后是一样的!
  • "tan""ant"

5. if (!m.has(sortedS)) {

if (!m.has(sortedS)) {
  • if:表示"如果...那么..."
  • !:表示"不是"、"不存在"
  • m.has(sortedS):询问分类柜 m:"你有没有叫 sortedS 这个钥匙的抽屉?"

如果 sortedS = "aet"

  • m.has("aet") → 问:"有没有钥匙叫 'aet' 的抽屉?"
  • !m.has("aet") → "如果没有的话..."

6. m.set(sortedS, []);

m.set(sortedS, []);
  • m.set(key, value):往分类柜里放东西
  • sortedS:钥匙(比如 "aet"
  • []:一个空数组(就像一个空盒子)

m.set("aet", []) → 创建一个钥匙叫 "aet" 的抽屉,里面放一个空盒子

7. m.get(sortedS).push(s);

m.get(sortedS).push(s);
  • m.get(sortedS):拿到钥匙 sortedS 对应的抽屉里的盒子
  • .push(s):把单词 s 放进这个盒子

如果 sortedS = "aet", s = "eat"

  • m.get("aet") → 拿到装着字母异位词的盒子
  • .push("eat") → 把 "eat" 放进去

8. return Array.from(m.values());

return Array.from(m.values());
  • m.values():拿出分类柜里所有抽屉里的东西(所有装着单词的盒子)
  • Array.from():把这些东西变成一个数组
  • return:把结果交给外面的人

实际运行过程演示

让我们用 ["eat", "tea", "tan"] 来追踪整个过程:

步骤ssortedSm 的状态操作
1"eat""aet"{}m.set("aet", []){"aet": []}
m.get("aet").push("eat"){"aet": ["eat"]}
2"tea""aet"{"aet": ["eat"]}m.has("aet") 为 true,跳过 set
m.get("aet").push("tea"){"aet": ["eat", "tea"]}
3"tan""ant"{"aet": ["eat", "tea"]}m.has("ant") 为 false
m.set("ant", []){"aet": ["eat", "tea"], "ant": []}
m.get("ant").push("tan"){"aet": ["eat", "tea"], "ant": ["tan"]}

最后 m.values() 得到:[["eat", "tea"], ["tan"]]


核心思想总结

"字母异位词"排序后完全一样,所以可以用排序后的字符串作为"分类标签"

就像:

  • "eat", "tea", "ate" 都属于 "aet" 类别
  • "tan", "nat" 都属于 "ant" 类别

JavaScript 语法小知识

语法含义例子
const声明常量const x = 5;
new Map()创建哈希表const map = new Map();
for...of遍历数组for(const item of array)
split()拆分字符串"abc".split('') → ["a", "b", "c"]
sort()排序["c", "a", "b"].sort() → ["a", "b", "c"]
join()连接数组["a", "b", "c"].join('') → "abc"
push()添加元素arr.push(item)
has()检查键是否存在map.has(key)
set()设置键值对map.set(key, value)
get()获取值map.get(key)

总结

这个算法巧妙地利用了:

  1. 排序:让字母异位词变得"一样"
  2. 哈希表:快速分类存储
  3. 循环:逐个处理每个单词

最终实现了高效的字母异位词分组!



你还在学习 JavaScript 的路上吗?在评论区分享你遇到的困惑,我们一起讨论!

#javascript #算法 #前端开发 #面试准备 #编程入门