【力扣-49.字母异位词分组】Python笔记

0 阅读3分钟

一、问题背景

字母异位词:由相同字母组成、但字母排列顺序不同的单词(如 "eat""tea""ate")。

目标:将数组中所有互为字母异位词的单词分组到一起。

示例:

  • 输入["eat","tea","tan","ate","nat","bat"]
  • 输出[["bat"],["nat","tan"],["ate","eat","tea"]]

二、解法一:排序字符串作为键(推荐)

核心思路

  • 互为异位词的字符串,排序后结果完全相同(如 "eat""tea" 排序后均为 "aet")。
  • 用排序后的字符串作为 key,原字符串作为 value,存入字典分组。
  • 时间复杂度:O(n * k log k) (n 为单词总数,k 为单词最大长度)
  • 空间复杂度:O(n * k)

from collections import defaultdict 
class Solution: 
    def groupAnagrams(self, strs: list[str]) -> list[list[str]]:
    # 增强版字典:当访问不存在的键时,自动创建空列表作为默认值 
    d = defaultdict(list) 
    
    for s in strs: 
        # 对字符串排序,生成唯一key 
        sorted_str = "".join(sorted(s)) 
        # 将原字符串加入对应分组 
        d[sorted_str].append(s)
    # 取出字典中所有值,即为分组结果 
    return list(d.values())

关键知识点

  • defaultdict(list):避免手动判断 key 是否存在,直接调用 append()
  • sorted(s):对任意序列排序,返回列表;"".join(...) 将列表拼接为字符串。
  • 示例:sorted("tea") → ['t','e','a']"aet"

三、解法二:字符计数作为键(进阶)

核心思路

  • 字母仅为小写(26 个),用长度为 26 的数组统计每个字母出现次数。
  • 将计数数组转换为元组(可哈希)作为 key,原字符串作为 value 分组。
  • 时间复杂度:O(n * k) (避免排序,计数为线性操作)
  • 空间复杂度:O(n * k)
from collections import defaultdict 
class Solution: 
    def groupAnagrams(self, strs: list[str]) -> list[list[str]]:
    # 创建一个默认值为列表的字典 
    anagram_map = defaultdict(list) 
    for s in strs: 
    # 初始化26个0的计数数组,对应a-z 
        count = [0] * 26 
        for char in s: 
            # 将字符映射到0-25的索引,并计数 
            count[ord(char) - ord('a')] += 1 
            # 列表不可哈希,转换为元组作为key 
            anagram_map[tuple(count)].append(s) 
    # 返回所有分组 
    return list(anagram_map.values())

关键知识点

  • ord(char) - ord('a'):将字符 a-z 映射到 0-25 的索引。
  • 元组 tuple(count):列表不可作为字典键,元组可哈希,适合作为 key
  • 优势:无需排序,在单词较长时性能更优。

四、核心 API 与技巧总结

函数 / 类作用
defaultdict(list)自动创建空列表作为默认值,简化分组逻辑
sorted(s)对字符串排序,生成异位词的唯一标识
ord(c)返回字符的 Unicode 编码,用于字符计数
tuple(list)将列表转为可哈希的元组,作为字典键
dict.values()取出字典中所有值,得到最终分组

五、两种解法对比

维度排序法计数法
实现复杂度简单,代码更短稍复杂,需手动计数
时间效率O(nk log k)O (nk),长文本更优
适用场景通用场景,代码易读已知字符范围(如小写字母)
空间占用O(nk)O(nk)