字母异位词分组 图解题解
字母一样、顺序乱的词如何自动归堆?给每个词做一个「排序身份证」,哈希表帮你一步到位。
想象你是图书馆管理员,手里有一堆书,书名顺序各异但用字相同。与其两两对比书名,不如把每本书的书名字母排序后当「分类码」,贴到书脊上——分类码一样的直接推进同一个架格。哈希表就是这排书架,同一个 key 的词自动落进同一个桶,不用逐组比对。
这道题到底在问什么
- 输入
- strs=["eat","tea","tan","ate","nat","bat","tab","ant","abt"]
- 输出
- 3 组
最优解:为什么这么做
一句话答案:LeetCode 49 字母异位词分组的通用解法是排序键加哈希表:把每个单词的字母排序得到规范 key,互为异位词的单词排序后必然相同,用哈希表把相同 key 的单词收进同一组,一次遍历完成分组,时间 O(n·k·log k)、空间 O(n·k);用 26 字母计数当 key 还能进一步降到 O(n·k)。
这道题真正在问什么
题目给一组单词,要求把「字母完全相同、只是排列顺序不同」的单词归到同一组,返回全部分组,例如 eat、tea、ate 是一组。它本质不是在问「两个词是不是异位词」,而是在问「怎么给 n 个单词按异位关系划分等价类」——想清楚这一点,就能看出两两比较的思路从一开始就走偏了。
为什么不能两两比较判断异位词
最直觉的做法是拿每对单词比一比,统计两边字母出现次数是否一致。判断一对是 O(k)(k 为单词长度),但 n 个单词约有 n²/2 对,整体 O(n²·k);而且比完还得靠并查集之类的结构才能落成分组,又慢又绕。
换个角度想:分组问题的通用套路是给每个元素算一个「指纹」——同组指纹必然相同、不同组必然不同,然后按指纹归堆。只要指纹算得快,分组就退化成一次哈希表遍历。于是问题变成:什么指纹能唯一刻画「互为异位词」这层等价关系?
为什么排序后的字符串能当 key
两个单词互为异位词,等价于「每个字母出现的次数完全相同」。把字母排序恰好抹掉顺序差异、只保留字母及其个数:eat、tea、ate 排序后都是 aet;反过来,排序结果相同也必然意味着每种字母个数相同。所以「排序后的字母序列」与「互为异位词」是充要条件,拿它当哈希表的 key 既不会错分也不会漏分。注意不能偷懒成「字母集合」——aab 和 ab 的字母集合相同,但出现次数不同,并不是异位词。
哈希表怎么一次遍历完成分组
建一张表,key 是排序后的字母序列,value 是这一组单词的列表。从左到右扫一遍:每个单词先排序算出 key,表里没有这个 key 就新建一组放进去,有就追加到已有的组。这里的关键动作是「追加」而不是「赋值」——Python 写成 groups.setdefault(key, []).append(w),如果直接对 key 赋新列表,会把同组先来的单词整组覆盖丢掉。扫完把表里所有的组收集起来就是答案。
复杂度怎么算,还能更快吗
时间 O(n·k·log k):n 个单词各排序一次,每次 O(k·log k),哈希插入均摊 O(1)。空间 O(n·k):哈希表要存下全部单词。
想去掉排序的 log,可以换一种指纹:统计 26 个字母各出现几次,拼成形如 a2b1 的计数串当 key,构造一个 key 只要 O(k),整体降到 O(n·k)。两种 key 都正确:排序版更好写,计数版在单词较长时更快,面试里说清这层取舍就是加分项。
▶ 动画逐步走查(共 21 步)——想跟着动画一帧帧对照就展开
- 3记住这条:排序后字母序列相同的单词,就是同一组异位词。
- 4上面是单词数组(下标固定)。分组表一开始是空的,从左到右逐个处理每个单词。
- 5处理单词 'eat':把它的字母排序,得到 key = "aet"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 6分组表里还没有 key "aet",说明 'eat' 是这一组的第一个单词,新建一组把它放进去。
- 7处理单词 'tea':把它的字母排序,得到 key = "aet"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 8分组表里已经有 key "aet" 了,说明前面出现过它的异位词,把 'tea' 追加到同一组。
- 9处理单词 'tan':把它的字母排序,得到 key = "ant"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 10分组表里还没有 key "ant",说明 'tan' 是这一组的第一个单词,新建一组把它放进去。
- 11处理单词 'ate':把它的字母排序,得到 key = "aet"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 12分组表里已经有 key "aet" 了,说明前面出现过它的异位词,把 'ate' 追加到同一组。
- 13处理单词 'nat':把它的字母排序,得到 key = "ant"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 14分组表里已经有 key "ant" 了,说明前面出现过它的异位词,把 'nat' 追加到同一组。
- 15处理单词 'bat':把它的字母排序,得到 key = "abt"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 16分组表里还没有 key "abt",说明 'bat' 是这一组的第一个单词,新建一组把它放进去。
- 17处理单词 'tab':把它的字母排序,得到 key = "abt"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 18分组表里已经有 key "abt" 了,说明前面出现过它的异位词,把 'tab' 追加到同一组。
- 19处理单词 'ant':把它的字母排序,得到 key = "ant"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 20分组表里已经有 key "ant" 了,说明前面出现过它的异位词,把 'ant' 追加到同一组。
- 21处理单词 'abt':把它的字母排序,得到 key = "abt"。等会儿就拿这个 key 去分组表里找它该归哪一组。
- 22分组表里已经有 key "abt" 了,说明前面出现过它的异位词,把 'abt' 追加到同一组。
- 23全部处理完,分组表里有 3 个 key,就是 3 组异位词——把每个 key 对应的单词列表收集起来就是答案。
⚠️ 容易写错的地方
✗ 错:用单词本身当 key
✓ 对:要用排序后的字母序列当 key
顺序不同的异位词原串不同、归不到一起
✗ 错:只比字母集合(去重)
✓ 对:要保留每个字母出现次数
"aab" 和 "ab" 字母集合相同但不是异位词
✗ 错:排序产生新 key 却覆盖旧组
✓ 对:用 setdefault/computeIfAbsent 追加
直接赋值会丢掉同组已有单词
完整代码(Python / C++ / Java)
Python
def groupAnagrams(strs):
groups = {} # key -> 单词组
for w in strs:
key = "".join(sorted(w)) # 排序后的字母序列
groups.setdefault(key, []).append(w)
return list(groups.values())C++
vector<vector<string>> groupAnagrams(vector<string>& strs){
unordered_map<string, vector<string>> groups;
for(auto& w : strs){
string key = w;
sort(key.begin(), key.end()); // 排序得 key
groups[key].push_back(w);
}
vector<vector<string>> res;
for(auto& p : groups) res.push_back(p.second);
return res;
}Java
List<List<String>> groupAnagrams(String[] strs){
Map<String, List<String>> groups = new HashMap<>();
for (String w : strs) {
char[] cs = w.toCharArray();
java.util.Arrays.sort(cs);
String key = new String(cs); // 排序得 key
groups.computeIfAbsent(key, x -> new ArrayList<>()).add(w);
}
return new ArrayList<>(groups.values());
}复杂度
时间
O(n·k·log k)
n 个单词,每个长 k,排序 k·log k
空间
O(n·k)
哈希表存下所有单词
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 字母异位词分组 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
除了排序,还能用什么当 key?+
用 26 个字母的计数构成的元组/字符串当 key,例如 "a2b1c0..."。这样把每个 key 的构造从 O(k·log k) 降到 O(k),整体 O(n·k)。
为什么相同 key 一定互为异位词?+
排序后字母序列相同 ⇔ 两个单词每个字母出现次数都相同 ⇔ 互为异位词,这是充要条件。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 字母异位词分组 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。