LeetCode-day2:字母异位词分组分析

博客:从「字母异位词分组」中学到的 Python 哈希与字符串处理核心技巧

题号:LeetCode #49 Group Anagrams

语言:Python

核心收获:如何设计"键(key)"来分组数据 + 深入理解 sorteddefaultdictordtuple 等关键工具


一、题目回顾

给定一个字符串数组 strs,将所有字母异位词(Anagram)组合在一起。

  • 字母异位词 :由相同字母以不同顺序组成的单词(如 "eat""tea""ate"
  • 要求:返回一个列表,每个子列表包含一组异位词

示例:

python 复制代码
输入: ["eat","tea","tan","ate","nat","bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

二、解题核心思想:用"标准化签名"作为分组依据

要判断两个字符串是否是异位词,关键是看它们包含的字母种类和数量是否完全一致

于是我们引入一个核心策略:

为每个字符串生成一个唯一的"签名(signature)",只要签名相同,就是异位词。

然后用哈希表(字典) 把相同签名的字符串归为一组。

接下来,我通过这道题学会了三种生成签名的方法,每一种都带来了新的 Python 技巧。


三、方法一:排序法 ------ 学会 ''.join(sorted(s))

✅ 核心代码:

python 复制代码
key = ''.join(sorted(s))

🔍 我学到的关键点:

  1. sorted(s) 对字符串排序后返回的是字符列表

    python 复制代码
    sorted("bac")  # → ['a', 'b', 'c'](不是 "abc"!)

    因为 sorted() 函数对任何可迭代对象操作后统一返回 list,这是 Python 的设计规范。

  2. 要用 ''.join(...) 把字符列表拼回字符串

    python 复制代码
    ''.join(['a','b','c'])  # → "abc"

    这是字符串拼接的高效方式 (比 + 快得多)。

  3. 这个字符串可作为字典的 key

    因为字符串是不可变类型,天然可哈希。

💡 应用场景:

  • 所有需要"忽略顺序、只看组成"的问题(如异位词、变位数等)

四、方法二:使用 defaultdict ------ 告别手动初始化!

✅ 核心代码:

python 复制代码
from collections import defaultdict
groups = defaultdict(list)
groups[key].append(s)  # 即使 key 不存在,也会自动创建空列表!

🔍 我学到的关键点:

  1. 传统写法很啰嗦

    python 复制代码
    if key not in groups:
        groups[key] = []
    groups[key].append(s)
  2. defaultdict(list) 自动处理"第一次出现"的情况

    • 当访问一个不存在的 key 时,它会自动调用 list() 创建一个空列表
    • 然后你可以直接 .append(),无需判断
  3. defaultdictdict 的子类,用法几乎一样

    最后仍可通过 list(groups.values()) 获取结果

💡 扩展知识:

  • defaultdict(int):用于计数(自动初始化为 0)
  • defaultdict(set):用于去重集合
  • 这是 LeetCode 分组类题目的标准模板

五、方法三:字符频次法 ------ 学会 ordtuple 作 key

✅ 核心代码:

python 复制代码
count = [0] * 26  # 初始化长度为 26 的全零列表
for char in s:
    count[ord(char) - ord('a')] += 1
key = tuple(count)  # 转为元组才能当 dict 的 key

🔍 我学到的关键点:

1. [0] * 26 快速创建固定长度列表
  • 这是初始化计数数组的惯用写法
  • for i in range(26): count.append(0) 更简洁高效
2. ord() 函数获取字符的 ASCII 码
  • ord('a') → 97

  • ord('b') → 98

  • 所以 ord(char) - ord('a') 可将小写字母映射到 0~25

    python 复制代码
    'a' → 0, 'b' → 1, ..., 'z' → 25
3. 为什么不用 if 判断?------ 数组下标天然支持"分类"
  • 以前我总想:"如果遇到 'a' 就加到 a 的计数,遇到 'b' 就加到 b......"
  • 但其实不需要 if !直接用 count[index] += 1 即可
  • 这是用空间换逻辑简化的经典思想
4. tuple(count) 让列表变成可哈希的 key
  • 字典的 key 必须是不可变类型

  • list 是可变的 → 不能当 key

  • tuple 是不可变的 → 可以当 key

    python 复制代码
    d = {}
    d[[1,2]] = 1   # ❌ 报错
    d[(1,2)] = 1   # ✅ 成功

💡 优势 vs 排序法:

  • 时间复杂度更低:O(M) vs O(M log M)(M 是字符串长度)
  • 但仅适用于小写字母等有限字符集

六、完整代码对比

✅ 方法一:排序法(推荐,简洁通用)

python 复制代码
from collections import defaultdict
from typing import List

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        groups = defaultdict(list)
        for s in strs:
            key = ''.join(sorted(s))
            groups[key].append(s)
        return list(groups.values())

✅ 方法三:频次法(高效,适合面试展示深度)

python 复制代码
from collections import defaultdict
from typing import List

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        groups = defaultdict(list)
        for s in strs:
            count = [0] * 26
            for char in s:
                count[ord(char) - ord('a')] += 1
            groups[tuple(count)].append(s)
        return list(groups.values())

七、本题带给我的核心认知升级

以前的我 现在的我
想手写排序或用一堆 if 判断字符 知道用 sorted + joinord + 数组计数
手动检查 key 是否存在再初始化 defaultdict 自动处理
不知道 list 不能当 dict 的 key 理解"可哈希"概念,会用 tuple 转换
对字符串操作不熟 掌握 sorted(s) 返回列表、''.join() 拼接

八、延伸思考

  1. 如果字符串包含大写字母、数字、符号怎么办?

    • 排序法依然适用 ✅
    • 频次法需扩展数组大小或改用 dict 计数
  2. 能否不用哈希表?

    • 可以,但时间复杂度会退化到 O(N²M),不实用
相关推荐
熬夜敲代码的小N2 小时前
2026 职场生存白皮书:Gemini Pro 实战使用指南
人工智能·python·ai·职场和发展
NAGNIP2 小时前
Hugging Face 200页的大模型训练实录
人工智能·算法
Swift社区2 小时前
LeetCode 457 - 环形数组是否存在循环
算法·leetcode·职场和发展
白帽黑客-晨哥3 小时前
Web安全方向的面试通常会重点考察哪些漏洞和防御方案?
安全·web安全·面试·职场和发展·渗透测试
2401_877274243 小时前
2025数据结构实验八:排序
数据结构·算法·排序算法
J2虾虾3 小时前
空间矢量数据结构及其表达
算法
Neil今天也要学习3 小时前
永磁同步电机无速度算法--永磁同步电机转子位置精确估计的误差抑制方法
算法
Irene19913 小时前
JavaScript 常见算法复杂度总结(大O表示法)
javascript·算法
开心比对错重要3 小时前
进程、线程、虚拟线程详解及线程个数设置
java·jvm·算法·面试