字母异位词分组
· 阅读需 6 分钟
力扣面试经典——49题
💡 参考代码:
#define MAX_STR_SIZE 100
typedef struct {
int len;
int idx;
char str[MAX_STR_SIZE + 1];
} sort_str_t;
int cmp1(const void *a, const void *b)
{
char x = *(char *)a;
char y = *(char *)b;
return x - y;
}
int cmp2(const void *a, const void *b)
{
sort_str_t *x = (sort_str_t *)a;
sort_str_t *y = (sort_str_t *)b;
int diff = strcmp(x->str, y->str);
if (diff != 0) {
return diff;
}
return x->idx - y->idx;
}
/**
* 将字母异位词组合在一起
* @param strs 输入的字符串数组
* @param strsSize 字符串数组的长度
* @param returnSize 返回结果的行数
* @param returnColumnSizes 返回每行的列数
* @return 分组后的字母异位词二维数组
*/
char*** groupAnagrams(char** strs, int strsSize, int* returnSize, int** returnColumnSizes) {
// 处理边界情况
if (strsSize == 0) {
*returnSize = 0;
*returnColumnSizes = NULL;
return NULL;
}
// 分配返回结果的内存空间
char ***ret = (char ***)malloc(strsSize * sizeof(char **));
*returnColumnSizes = (int *)malloc(strsSize * sizeof(int));
sort_str_t *hash_array = (sort_str_t *)malloc(strsSize * sizeof(sort_str_t));
// 初始化结构体数组:复制原字符串并排序
for (int i = 0; i < strsSize; i++) {
hash_array[i].len = strlen(strs[i]);
hash_array[i].idx = i;
strcpy(hash_array[i].str, strs[i]);
// 对每个字符串的字符进行排序
qsort(hash_array[i].str, hash_array[i].len, sizeof(char), cmp1);
}
// 对结构体数组按排序后的字符串进行排序
qsort(hash_array, strsSize, sizeof(sort_str_t), cmp2);
// 按排序后的结果进行分组
int ret_size = 0;
int j = 0;
while (j < strsSize) {
int k = j + 1;
// 找到所有具有相同排序字符串的元素
while (k < strsSize && !strcmp(hash_array[j].str, hash_array[k].str)) {
k++;
}
// 计算当前分组的大小
int group_size = k - j;
(*returnColumnSizes)[ret_size] = group_size;
// 为当前分组分配内存
ret[ret_size] = (char **)malloc(group_size * sizeof(char *));
// 填充当前分组的数据
for (int i = 0; i < group_size; i++) {
ret[ret_size][i] = (char *)malloc((hash_array[j].len + 1) * sizeof(char));
strcpy(ret[ret_size][i], strs[hash_array[i + j].idx]);
}
ret_size++;
j += group_size;
}
*returnSize = ret_size;
// 释放临时内存
free(hash_array);
return ret;
}
📖 总结:
点击展开题目总结
🤔 字母异位词分组
1️⃣ 题目核心信息
🎯 功能描述:将字符串数组中互为字母异位词的字符串归为一组
📥 输入输出:
- 输入:
strs: 字符串数组strsSize: 字符串数组长度
- 输出:
- 返回分组后的二维字符串数组
returnSize: 返回结果行数returnColumnSizes: 返回每行列数的数组
2️⃣ 实现原理
💡 核心思路:利用字母异位词排序后相同的特性,通过排序和分组实现归类
📋 实现步骤:
- 对每个字符串内部字符进行排序,生成统一的键值
- 将所有排序后的字符串与原始索引一起保存
- 对这些结构体按排序后的字符串进行排序
- 遍历排序后的数组,将相同键值的字符串归为一组
3️⃣ 关键点解析
🎯 代码技巧
- 使用结构体保存原始索引和排序后的字符串,便于后续分组
- 两次排序:先对字符串内部排序,再对整个数组排序
- 线性扫描分组:排序后相同元素必然相邻,只需遍历分组
4️⃣ 使用场景
✅ 适用情况:
- 需要对具有相同元素但顺序不同的数据进行分组
- 字符串处理和分类问题
- 数据去重和归类操作
⚠️ 前提条件:
- 输入字符串只包含小写字母
- 需要保持相同字母异位词在同一组
5️⃣ 复杂度分析
-
⏱️ 时间复杂度:O(N * M * log M + N * log N)
- N为字符串数量,M为字符串平均长度
- 每个字符串内部排序:O(M * log M)
- 结构体数组排序:O(N * log N)
-
💾 空间复杂度:O(N * M)
- N为字符串数量,M为字符串平均长度
- 需要额外空间存储结构体数组和结果数组
6️⃣ 注意事项
🚩 边界情况:
- 空字符串数组
- 包含空字符串的数组
- 只有一个字符串的数组
💥 易错点:
- 忘记保存原始索引导致无法获取原始字符串
- 内存分配后未正确释放导致内存泄漏
- 分组时边界条件处理错误
7️⃣ 补充说明
以输入数组:["eat", "tea", "tan", "ate", "nat", "bat"]为例
执行步骤详解
第一步:初始化结构体数组
创建 sort_str_t 结构体数组,对每个字符串进行内部排序:
索引0: "eat" -> 排序后 "aet"
索引1: "tea" -> 排序后 "aet"
索引2: "tan" -> 排序后 "ant"
索引3: "ate" -> 排序后 "aet"
索引4: "nat" -> 排序后 "ant"
索引5: "bat" -> 排序后 "abt"
此时 hash_array 内容:
hash_array[0]: {len=3, idx=0, str="aet"}
hash_array[1]: {len=3, idx=1, str="aet"}
hash_array[2]: {len=3, idx=2, str="ant"}
hash_array[3]: {len=3, idx=3, str="aet"}
hash_array[4]: {len=3, idx=4, str="ant"}
hash_array[5]: {len=3, idx=5, str="abt"}
第二步:对结构体数组排序
按照 str 字段进行排序(如果相同则按 idx 排序):
排序后 hash_array 内容:
hash_array[0]: {len=3, idx=5, str="abt"} // 对应 "bat"
hash_array[1]: {len=3, idx=2, str="ant"} // 对应 "tan"
hash_array[2]: {len=3, idx=4, str="ant"} // 对应 "nat"
hash_array[3]: {len=3, idx=0, str="aet"} // 对应 "eat"
hash_array[4]: {len=3, idx=1, str="aet"} // 对应 "tea"
hash_array[5]: {len=3, idx=3, str="aet"} // 对应 "ate"
第三步:分组处理
分组处理过程
遍历排序后的数组,将相同字符串的归为一组:
-
处理 "abt" 组:
- 只有索引5("bat")
- 创建第一组:["bat"]
-
处理 "ant" 组:
- 索引2("tan")和索引4("nat")
- 创建第二组:["tan", "nat"]
-
处理 "aet" 组:
- 索引0("eat")、索引1("tea")和索引3("ate")
- 创建第三组:["eat", "tea", "ate"]
最终结果
返回结果:
[
["bat"],
["tan", "nat"],
["eat", "tea", "ate"]
]
这个算法通过排序将字母异位词转换为相同的形式,然后利用排序后数组中相同元素必然相邻的特性,实现了高效的分组操作。
加载评论中...
