目录
C 语言实现字母异位词分组
在本篇博客中,我们将介绍如何用 C 语言实现力扣第 49 题——字母异位词分组(Group Anagrams)。题目要求将一组字符串分成多个组,其中每组字符串中的所有元素都是字母异位词。我们将逐行分析代码的实现过程,并详细解释每个部分的功能。
题目分析
字母异位词(Anagram)是指两个字符串包含相同的字符,且字符的频率相同。比如,"eat"
, "tea"
, "ate"
就是字母异位词组。我们的目标是将一组字符串分组,找到所有的字母异位词,并将它们归为一组。
代码实现
1. 包含必要的头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STR_LEN 100
stdio.h
:提供输入输出函数,如printf
用于打印结果。stdlib.h
:提供内存管理函数,如malloc
和free
,以及排序函数qsort
。string.h
:提供字符串操作函数,如strlen
和strcmp
。MAX_STR_LEN
:定义了字符串的最大长度,用于内存分配时的估算。
2. 排序比较函数
int compare(const void *a, const void *b) {
return strcmp((char*)a, (char*)b);
}
- 作用:
compare
是用于排序的比较函数。qsort
函数需要一个比较函数来确定排序规则。此函数使用strcmp
来比较两个字符串的字典序。 - 解释:
qsort
函数需要将传入的指针转换为char*
类型,比较这两个字符串的字典序。
3. 字符串排序函数
void sort_string(char *str) {
qsort(str, strlen(str), sizeof(char), compare);
}
- 作用:此函数使用
qsort
对给定的字符串进行排序。排序后的字符串会帮助我们将字母异位词归类到一起。 - 解释:
qsort
函数排序字符串时,使用字符串的长度strlen(str)
来确定排序的长度,并以字符为单位排序。
4. 主函数 groupAnagrams
char*** groupAnagrams(char** strs, int strsSize, int* returnSize, int** returnColumnSizes) {
- 作用:这是实现字母异位词分组的核心函数。该函数返回一个三维指针数组,表示不同字母异位词的组。
- 参数:
strs
:输入字符串数组。strsSize
:字符串数组的大小。returnSize
:指向整数的指针,用于返回结果数组的大小(即异位词组的数量)。returnColumnSizes
:指向整数数组的指针,返回每个字母异位词组的大小。
5. 初始化变量
char* hash_map[10000]; // 假设最多10000个不同的字母异位词
int hash_map_size = 0;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * strsSize);
char*** ans = (char***)malloc(sizeof(char**) * strsSize);
- 作用:初始化一些数据结构和变量:
hash_map
:用于存储已排序的字符串,作为字母异位词的标识符。hash_map_size
:记录哈希表的大小,即当前已发现的异位词组数量。returnSize
:初始化返回的字母异位词组的数量。returnColumnSizes
:为每个字母异位词组分配内存,记录每组字符串的数量。ans
:动态分配内存来存储最终的分组结果。
6. 遍历所有字符串,排序并分组
for (int i = 0; i < strsSize; i++) {
char *sorted_str = strdup(strs[i]); // 复制当前字符串
sort_string(sorted_str);
- 作用:遍历输入字符串数组,为每个字符串进行排序。
- 解释:
strdup
创建一个新的字符串副本,这样我们可以安全地对其进行排序,而不修改原始字符串。
7. 查找是否已存在该字母异位词组
int found = -1;
for (int j = 0; j < hash_map_size; j++) {
if (strcmp(sorted_str, hash_map[j]) == 0) {
found = j;
break;
}
}
- 作用:检查当前排序后的字符串是否已经出现在哈希表中。
- 解释:如果找到匹配的字符串(即字母异位词组已存在),则将其索引保存到
found
。
8. 处理新发现的字母异位词组
if (found == -1) {
hash_map[hash_map_size] = sorted_str;
ans[hash_map_size] = (char**)malloc(sizeof(char*) * strsSize);
(*returnColumnSizes)[hash_map_size] = 0;
found = hash_map_size;
hash_map_size++;
} else {
free(sorted_str); // 释放不再需要的内存
}
- 作用:如果找不到匹配的字母异位词组,说明这是一个新的字母异位词组。
- 解释:我们将新发现的排序字符串添加到
hash_map
中,并在ans
中为该组分配内存,初始化每组的大小。
9. 将当前字符串添加到字母异位词组中
ans[found][(*returnColumnSizes)[found]++] = strs[i];
}
- 作用:将当前字符串加入到对应的字母异位词组中。
- 解释:通过
found
找到对应的字母异位词组,增加该组的大小。
10. 返回结果
*returnSize = hash_map_size;
return ans;
}
- 作用:将结果赋值给
returnSize
,并返回字母异位词组的三维数组ans
。
11. main
函数
int main() {
char *param_1[] = {"eat", "tea", "tan", "ate", "nat", "bat"};
int size_1 = 6;
- 作用:在
main
函数中定义输入字符串数组param_1
和其大小size_1
。
12. 调用 groupAnagrams
并打印结果
int ret_size, *ret_colsize;
char ***ret = groupAnagrams(param_1, size_1, &ret_size, &ret_colsize);
printf("Total Groups: %d\n", ret_size);
for (int i = 0; i < ret_size; i++) {
printf("Group %d: ", i + 1);
for (int j = 0; j < ret_colsize[i]; j++) {
printf("%s ", ret[i][j]);
}
printf("\n");
}
- 作用:调用
groupAnagrams
函数,获取分组结果,并打印每个字母异位词组。 - 解释:输出字母异位词组的数量以及每组中的元素。
13. 内存释放
for (int i = 0; i < ret_size; i++) {
free(ret[i]);
}
free(ret_colsize);
free(ret);
return 0;
}
- 作用:释放在
groupAnagrams
中动态分配的内存。 - 解释:为了避免内存泄漏,在程序结束前释放所有分配的内存。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STR_LEN 100
// 比较两个字符串的排序结果
int compare(const void *a, const void *b) {
return strcmp((char*)a, (char*)b);
}
// 字符串排序函数
void sort_string(char *str) {
qsort(str, strlen(str), sizeof(char), compare);
}
// 主解法,返回结果作为函数的返回值
char*** groupAnagrams(char** strs, int strsSize, int* returnSize, int** returnColumnSizes) {
// 使用哈希表存储排序后的字符串和对应的字母异位词
char* hash_map[10000]; // 假设最多10000个不同的字母异位词
int hash_map_size = 0;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * strsSize);
// 临时存储每组字母异位词
char*** ans = (char***)malloc(sizeof(char**) * strsSize);
for (int i = 0; i < strsSize; i++) {
// 排序当前字符串
char *sorted_str = strdup(strs[i]); // 复制当前字符串
sort_string(sorted_str);
// 查找是否已存在该排序后的字符串
int found = -1;
for (int j = 0; j < hash_map_size; j++) {
if (strcmp(sorted_str, hash_map[j]) == 0) {
found = j;
break;
}
}
if (found == -1) {
// 新的字母异位词组
hash_map[hash_map_size] = sorted_str;
ans[hash_map_size] = (char**)malloc(sizeof(char*) * strsSize);
(*returnColumnSizes)[hash_map_size] = 0;
found = hash_map_size;
hash_map_size++;
} else {
free(sorted_str); // 释放不再需要的内存
}
// 将当前字符串加入对应的字母异位词组
ans[found][(*returnColumnSizes)[found]++] = strs[i];
}
*returnSize = hash_map_size;
return ans;
}
int main() {
// 输入
char *param_1[] = {"eat", "tea", "tan", "ate", "nat", "bat"};
int size_1 = 6;
// 输出结果
int ret_size, *ret_colsize;
// 调用 groupAnagrams 并直接接收返回值
char ***ret = groupAnagrams(param_1, size_1, &ret_size, &ret_colsize);
// 打印结果
printf("Total Groups: %d\n", ret_size);
for (int i = 0; i < ret_size; i++) {
printf("Group %d: ", i + 1);
for (int j = 0; j < ret_colsize[i]; j++) {
printf("%s ", ret[i][j]);
}
printf("\n");
}
// 释放内存
for (int i = 0; i < ret_size; i++) {
free(ret[i]);
}
free(ret_colsize);
free(ret);
return 0;
}