【Day43】49. 字母异位词分组

文章目录

49.字母异位词分组

题目:

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

示例 1 :
输入 : strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出 : [["bat"],["nat","tan"],["ate","eat","tea"]]
解释

在 strs 中没有字符串可以通过重新排列来形成 "bat"。

字符串 "nat" 和 "tan" 是字母异位词,因为它们可以重新排列以形成彼此。

字符串 "ate" ,"eat" 和 "tea" 是字母异位词,因为它们可以重新排列以形成彼此。

示例 2 :
输入 : strs = [""]
输出: [[""]]

示例 3 :
输入 : strs = ["a"]
输出: [["a"]]

提示

1 <= strs.length <= 10^4

0 <= strs[i].length <= 100

strs[i] 仅包含小写字母


思路(排序法√):

字母异位词:由相同字母、不同顺序组成的单词

比如:eat / tea / ate 是一组
tan / nat 是一组

要求:把所有互为字母异位词的单词分到同一个列表里。


用「排序后的字符串」做 key,把互为异位词的单词存在同一个 value 里

  1. 对每个字符串按字母排序
    • eat → 排序 → aet
    • tea → 排序 → aet
    • tan → 排序 → ant
  2. map 存储
    • key:排序后的字符串(唯一标识一组异位词)
    • value:这一组的所有单词
  3. 最后把 map 里的所有 value 拿出来,就是答案

代码实现(Go):

go 复制代码
package main

import (
	"fmt"
	"sort"
)

// groupAnagrams 字母异位词分组
// 核心思想:字母异位词排序后结果相同,用排序后的字符串作为唯一 key 进行分组
func groupAnagrams(strs []string) [][]string {
	// 定义哈希表(map)
	// key  :string 类型 → 排序后的字符串(同一组异位词的唯一标识)
	// value:[]string 类型 → 存储属于当前分组的所有原始字符串
	hashMap := make(map[string][]string)

	// 遍历输入字符串数组中的每一个字符串
	for _, str := range strs {
		// 将字符串转换为 byte 切片,方便排序
		// 示例:"eat" -> []byte{'e', 'a', 't'}
		bytes := []byte(str)

		// 对 byte 切片进行升序排序
		// 排序后:[]byte{'e','a','t'} → []byte{'a','e','t'}
		// 所有字母异位词排序后都会得到相同的 byte 切片
		// sort.Slice 通过自定义比较函数来排序切片,这里用 sort.Slice 对 byte 排序,本质是按 ASCII 升序
		sort.Slice(bytes, func(i, j int) bool {
			return bytes[i] < bytes[j]
		})

		// 将排序后的 byte 切片转回字符串,作为当前分组的 key
		// 示例:[]byte{'a','e','t'} → "aet"
		key := string(bytes)

		// 将当前字符串,添加到哈希表中对应 key 的分组里
		// 若 key 不存在,map 会自动创建;若已存在,则追加到切片末尾
		hashMap[key] = append(hashMap[key], str)
	}

	// 定义结果切片,用于存放最终的分组结果
	result := [][]string{}

	// 遍历哈希表,将所有分组依次加入结果切片
	for _, group := range hashMap {
		result = append(result, group)
	}

	// 返回最终分组结果
	return result
}

func main() {
	// 输入:待分组的字符串数组
	inputStrs := []string{"eat", "tea", "tan", "ate", "nat", "bat"}

	// 调用字母异位词分组函数,得到输出结果
	output := groupAnagrams(inputStrs)

	// 输出:[[eat tea ate] [tan nat] [bat]]
	fmt.Println(output)
}
  • 时间复杂度O(n * k log k)
    • n:字符串个数
    • k:单个字符串最大长度
    • 每个字符串排序:k log k
  • 空间复杂度O(n * k)
    • 存储所有字符串

思路(计数法×):

字母异位词 ↔ 字母出现次数完全一样

比如:

  • eat:a(1)、e(1)、t(1)
  • tea:a(1)、e(1)、t(1)
    次数一样 → 同一组
  1. 每个字符串用 长度 26 的数组 统计 a-z 出现次数
  2. 把这个计数数组变成唯一 key
  3. map 把 key 相同的单词放一起
  4. 最后输出所有组

代码实现(Go):

go 复制代码
package main

import "fmt"


// 字母异位词的字符出现次数一定完全相同,用次数数组作为 key 分组
func groupAnagrams(strs []string) [][]string {

	// map 的 key 是 [26]int 数组
	// 因为 Go 中数组可以直接比较,所以【完全相同的数组】会对应【同一个 key】
	// key:[26]int → 记录 a-z 每个字母出现的次数
	// value:[]string → 属于这一组的所有字母异位词
	mp := make(map[[26]int][]string)

	// 遍历输入的每一个字符串
	for _, str := range strs {

		// 1. 创建一个长度 26 的数组,用来统计当前字符串每个字母出现次数
		// 下标 0 → 'a',下标 1 → 'b' ... 下标 25 → 'z'
		cnt := [26]int{}

		// 2. 遍历当前字符串里的每一个字符
		for _, ch := range str {
			// ch - 'a' 把字符变成 0~25 的下标
			// 例:'a'-'a' = 0,'e'-'a' = 4
			// 然后对应位置计数 +1
			cnt[ch-'a']++
		}

		// 3. 核心:把【计数数组】当作 key
		// 只要两个字符串是字母异位词,cnt 数组一定一模一样 → key 相同 → 分到一组
		// 将当前字符串 str,添加到 map 中 key 为 cnt 对应的字符串切片中
		// 如果 cnt 不存在于 map 中,则自动创建 key=cnt,并将 str 作为切片的第一个元素
		mp[cnt] = append(mp[cnt], str)
	}

	// 4. 构造返回结果 
	// len(mp) = 最终结果的长度
	// 预先分配容量,效率更高,如果不写容量:Go 会自动扩容,不够就加,不够就加,频繁扩容变慢。
	// 创建一个空的二维字符串切片,但预先告诉 Go:这个切片未来要装 len(mp) 个元素,提前分配好空间
	res := make([][]string, 0, len(mp))

	// 遍历 map,把每一组单词放进结果里
	for _, group := range mp {
		res = append(res, group)
	}

	return res
}

func main() {
	// 输入:待分组的字符串数组
	inputStrs := []string{"eat", "tea", "tan", "ate", "nat", "bat"}

	// 调用字母异位词分组函数,得到输出结果
	output := groupAnagrams(inputStrs)

	// 输出:[[eat tea ate] [tan nat] [bat]]
	fmt.Println(output)
}

时间复杂度:O(N × K)

  • N:字符串个数
  • K:字符串最大长度
  • 每个字符只遍历一次,无排序,比排序法更快

空间复杂度:O(N × K)

  • 存储所有字符串
  • 计数数组固定大小 26,是常数空间

计数法虽然时间复杂度更优(O(n*k)),但它依赖固定字符集(如 a-z),通用性较差;而排序法适用于任意字符


相关推荐
参.商.2 小时前
【Day45】647. 回文子串 5. 最长回文子串
leetcode·golang
Trouvaille ~2 小时前
【优选算法篇】哈希表——空间换时间的极致艺术
c++·算法·leetcode·青少年编程·蓝桥杯·哈希算法·散列表
我是咸鱼不闲呀3 小时前
力扣Hot100系列22(Java)——[图论]总结(岛屿数量,腐烂的橘子,课程表,实现Trie(前缀树))
java·leetcode·图论
AMoon丶4 小时前
Golang--内存管理
开发语言·后端·算法·缓存·golang·os
阿Y加油吧4 小时前
力扣打卡——搜索二维矩阵、相交链表
线性代数·leetcode·矩阵
普贤莲花4 小时前
【2026年第11周---写于20260322】
程序人生·算法·leetcode
小白自救计划4 小时前
力扣知识点杂集
算法·leetcode·哈希算法
承渊政道5 小时前
【优选算法】(实战体验滑动窗口的奇妙之旅)
c语言·c++·笔记·学习·算法·leetcode·visual studio
lars_lhuan6 小时前
Go Context
golang