文章目录
- 242.有效的字母异位词
- 识别
- 核心/易错
- 难点/亮点
- 算法设计思路
- 代码实现
- 349. 两个数组的交集
- 识别
- 核心/易错
- 难点/亮点
- 算法设计思路
- 代码实现
- 改进建议
- 1.两数之和
- 识别
- 核心/易错
- 难点/亮点
- 算法设计思路
- 代码实现
- 详细分析
242.有效的字母异位词
识别
检查两个字符串是否为变位词(anagram)的C语言程序。变位词是指两个字符串由完全相同的字符组成,但字符的顺序可以不同。
核心/易错
核心功能是判断两个字符串是否为变位词。
易错点:
- 字符串长度不等时直接返回false,这是正确的,因为变位词的长度必须相同。
- 使用一个大小为26的数组来统计字符频率,假设输入只包含小写字母。如果输入包含大写字母或其他字符,程序将无法正确处理。
难点/亮点
难点在于如何高效地统计字符频率并比较两个字符串。亮点是使用一个固定大小的数组来存储字符频率,这种方法在时间复杂度上是高效的(O(n)),其中n是字符串的长度。
算法设计思路
- 首先检查两个字符串的长度是否相等,不等则直接返回false。
- 初始化一个大小为26的数组,用于统计每个字符的出现次数。
- 遍历第一个字符串,统计每个字符的出现次数。
- 遍历第二个字符串,从数组中减去每个字符的出现次数。如果在减去之前数组中的对应值已经为0,则说明第一个字符串中该字符的数量多于第二个字符串,直接返回false。
- 最后,遍历数组,如果所有元素都为0,则两个字符串是变位词,返回true;否则返回false。
代码实现
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool isAnagram(char* s, char* t) {
int len1 = strlen(s), len2 = strlen(t);
if (len1 != len2) {
return false;
}
int freq[26] = {0};
for (int i = 0; i < len1; i++) {
freq[s[i] - 'a'] += 1;
}
for (int i = 0; i < len2; i++) {
if (freq[t[i] - 'a'] == 0) {
return false;
}
freq[t[i] - 'a'] -= 1;
}
for (int i = 0; i < 26; i++) {
if (freq[i] != 0) {
return false;
}
}
return true;
}
int main() {
char s[] = "anagram";
char t[] = "nagaram";
if (isAnagram(s, t)) {
printf("'%s' and '%s' are anagrams.\n", s, t);
} else {
printf("'%s' and '%s' are not anagrams.\n", s, t);
}
return 0;
}
349. 两个数组的交集
识别
这段代码的目的是找出两个整数数组 nums1
和 nums2
的交集。它使用哈希表来存储 nums1
中的元素,然后遍历 nums2
,检查每个元素是否存在于哈希表中,如果存在并且不在结果数组中,则将其添加到结果数组中。
核心/易错
核心逻辑是使用哈希表来快速查找元素是否存在于 nums1
中,这是一个常见的优化策略,可以显著提高查找效率。易错点在于:
- 哈希表的初始化和使用:需要确保哈希表的大小足够大,以避免哈希冲突。
- 处理负数:通过添加偏移量
5000
来处理负数,确保它们在哈希表中有有效的索引。 - 去重:在将元素添加到结果数组之前,需要检查该元素是否已经存在,以避免重复。
难点/亮点
难点在于如何高效地处理可能存在的大量数据和避免重复元素。亮点是使用哈希表来优化查找过程,这通常比直接遍历数组要快得多。
算法设计思路
- 使用一个大小为
10001
的哈希表来存储nums1
中的元素,索引为元素值加上5000
(以处理负数)。 - 遍历
nums2
,对于每个元素,检查它是否在哈希表中存在,并且不在结果数组中。 - 如果满足条件,则将其添加到结果数组中,并更新结果数组的大小。
代码实现
#include <stdio.h>
#include <stdlib.h>
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size,
int* returnSize) {
// 创建一个哈希表,用于存储nums1中的元素
int* hashTable = (int*)calloc(10001, sizeof(int)); // 假设数组元素范围为0到10000
// 遍历nums1,将元素添加到哈希表中
for (int i = 0; i < nums1Size; i++) {
hashTable[nums1[i] + 5000] = 1; // 使用一个偏移量,防止负数问题
}
// 创建一个数组,用于存储结果
int* result = (int*)malloc(nums1Size * sizeof(int));
int resultSize = 0;
// 遍历nums2,检查元素是否在哈希表中
for (int i = 0; i < nums2Size; i++) {
if (hashTable[nums2[i] + 5000] == 1) {
// 检查结果数组中是否已经包含该元素
int isDuplicate = 0;
for (int j = 0; j < resultSize; j++) {
if (result[j] == nums2[i]) {
isDuplicate = 1;
break;
}
}
if (!isDuplicate) {
result[resultSize++] = nums2[i];
}
}
}
// 释放哈希表内存
free(hashTable);
*returnSize = resultSize;
return result;
}
改进建议
- 可以考虑使用动态数组(如
std::vector
在 C++ 中)来存储结果,以避免预先分配固定大小的数组。 - 如果
nums1
和nums2
的元素范围不是0
到10000
,需要调整哈希表的大小和偏移量。 - 可以考虑使用更高效的数据结构,如
std::unordered_set
(在 C++ 中),以进一步优化性能。
1.两数之和
识别
这段代码实现了一个经典的算法问题:两数之和。它使用哈希表(通过 uthash
库)来存储数组元素的值和它们的索引,以便快速查找两个数的和是否等于目标值。
核心/易错
核心功能是快速查找数组中是否存在两个数,它们的和等于给定的目标值。易错点包括:
- 正确管理内存分配和释放,避免内存泄漏。
- 确保哈希表操作正确,包括添加、查找和删除元素。
- 处理边界情况,如空数组或所有元素都不满足条件。
难点/亮点
难点在于如何高效地实现查找操作,亮点是使用哈希表来优化查找过程,将时间复杂度从可能的 O(n^2) 降低到 O(n)。
算法设计思路
- 初始化哈希表:使用
uthash
库创建一个哈希表,用于存储数组元素的值和它们的索引。 - 填充哈希表:遍历数组,将每个元素的值作为键,索引作为值添加到哈希表中。
- 查找目标:再次遍历数组,对于每个元素,计算目标值与当前元素值之差,然后在哈希表中查找这个差值。如果找到且对应的索引不是当前元素的索引,则找到了两个数的和等于目标值。
- 清理资源:在找到结果或遍历完成后,清理哈希表并释放所有分配的内存。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include "uthash.h"
typedef struct {
int key;
int value;
UT_hash_handle hh; // make this structure hashable
} map;
// 向哈希表中添加键值对
void hashMapAdd(map** hashMap, int key, int value) {
map* s;
HASH_FIND_INT(*hashMap, &key, s);
if (s == NULL) {
s = (map*)malloc(sizeof(map));
if (s == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
s->key = key;
s->value = value;
HASH_ADD_INT(*hashMap, key, s);
}
}
// 从哈希表中查找键对应的值
map* hashMapFind(map* hashMap, int key) {
map* s;
HASH_FIND_INT(hashMap, &key, s);
return s;
}
// 清理哈希表,释放所有内存
void hashMapCleanup(map** hashMap) {
map* cur;
map* tmp;
if (*hashMap != NULL) {
HASH_ITER(hh, *hashMap, cur, tmp) {
HASH_DEL((*hashMap), cur);
free(cur);
}
*hashMap = NULL;
}
}
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
int i, *ans;
map* hashMap = NULL; // 初始化哈希表为空
ans = malloc(sizeof(int) * 2); // 动态分配内存,用于存储两个索引
if (ans == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return NULL;
}
for (i = 0; i < numsSize; i++) {
// 将数组元素的值作为键,索引作为值添加到哈希表中
hashMapAdd(&hashMap, nums[i], i);
}
for (i = 0; i < numsSize; i++) {
// 查找target - nums[i]是否存在于哈希表中
map* hashMapRes = hashMapFind(hashMap, target - nums[i]);
if (hashMapRes && hashMapRes->value != i) { // 如果找到且索引不相同
ans[0] = i; // 存储第一个索引
ans[1] = hashMapRes->value; // 存储第二个索引
*returnSize = 2; // 设置返回数组的大小
hashMapCleanup(&hashMap); // 清理哈希表
return ans; // 返回结果
}
}
hashMapCleanup(&hashMap); // 清理哈希表
free(ans); // 释放内存
return NULL; // 如果没有找到结果,返回NULL
}
详细分析
-
初始化哈希表:
map* hashMap = NULL;
使用
NULL
初始化哈希表指针。 -
添加元素到哈希表:
void hashMapAdd(map** hashMap, int key, int value) { map* s; HASH_FIND_INT(*hashMap, &key, s); if (s == NULL) { s = (map*)malloc(sizeof(map)); if (s == NULL) { fprintf(stderr, "Memory allocation failed\n"); exit(EXIT_FAILURE); } s->key = key; s->value = value; HASH_ADD_INT(*hashMap, key, s); } }
- 使用
HASH_FIND_INT
宏查找哈希表中是否存在给定键的元素。 - 如果不存在,分配内存并添加到哈希表中。
- 使用
-
查找元素:
map* hashMapFind(map* hashMap, int key) { map* s; HASH_FIND_INT(hashMap, &key, s); return s; }
- 使用
HASH_FIND_INT
宏查找哈希表中是否存在给定键的元素。
- 使用
-
清理哈希表:
void hashMapCleanup(map** hashMap) { map* cur; map* tmp; if (*hashMap != NULL) { HASH_ITER(hh, *hashMap, cur, tmp) { HASH_DEL((*hashMap), cur); free(cur); } *hashMap = NULL; } }
- 使用
HASH_ITER
宏遍历哈希表中的所有元素,删除并释放内存。
- 使用
-
两数之和函数:
int* twoSum(int* nums, int numsSize, int target, int* returnSize) { int i, *ans; map* hashMap = NULL; ans = malloc(sizeof(int) * 2); if (ans == NULL) { fprintf(stderr, "Memory allocation failed\n"); return NULL; } for (i = 0; i < numsSize; i++) { hashMapAdd(&hashMap, nums[i], i); } for (i = 0; i < numsSize; i++) { map* hashMapRes = hashMapFind(hashMap, target - nums[i]); if (hashMapRes && hashMapRes->value != i) { ans[0] = i; ans[1] = hashMapRes->value; *returnSize = 2; hashMapCleanup(&hashMap); return ans; } } hashMapCleanup(&hashMap); free(ans); return NULL; }
- 初始化哈希表和结果数组。
- 遍历数组,将每个元素的值和索引添加到哈希表中。
- 再次遍历数组,查找是否存在一个元素的值加上当前元素的值等于目标值。
- 如果找到,将这两个元素的索引存储在结果数组中并返回。
- 如果没有找到,清理哈希表并返回
NULL
。
通过这些步骤,代码实现了两数之和的功能,同时确保了内存的正确管理。