本题有4个数组,较四数之和(一个数组),不用去重。
a+b的值先放入一个集合中,后遍历c+d的集合时判断,
元素数值可能很大,统计if出现及次数2个值。
计算四个数组中和为零的四元组数量。它使用了 uthash
库来实现哈希表,以高效地存储和查找数组元素的和。
核心部分在于正确使用 uthash
库来处理哈希表的创建、插入、查找和删除操作。
易错点包括:
malloc
分配内存失败的情况。HASH_FIND_INT
和 HASH_ADD_INT
宏来查找和添加哈希表项。难点在于理解和使用 uthash
库的宏来操作哈希表。
亮点是使用哈希表来减少查找时间,从而提高算法的效率。
nums1
和 nums2
数组中所有可能和的出现次数。nums1
和 nums2
数组,计算它们的和,并使用 insertOrUpdateHashTable
函数将这些和及其出现次数存储在哈希表中。nums3
和 nums4
数组,计算它们的和的相反数,并查找这个值在哈希表中的计数。如果找到,则将这个计数累加到结果中。#include
#include
struct hashTable {
int key;
int val;
UT_hash_handle hh;
};
// 释放哈希表中的所有元素
void freeHashTable(struct hashTable* ht) {
struct hashTable *current, *tmp;
HASH_ITER(hh, ht, current, tmp) {
HASH_DEL(ht, current);
free(current);
}
}
// 在哈希表中插入或更新元素
void insertOrUpdateHashTable(struct hashTable** ht, int key, int val) {
struct hashTable *tmp;
HASH_FIND_INT(*ht, &key, tmp);
if (tmp == NULL) {
tmp = malloc(sizeof(struct hashTable));
if (tmp == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
tmp->key = key;
tmp->val = val;
HASH_ADD_INT(*ht, key, tmp);
} else {
tmp->val += val;
}
}
int fourSumCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* nums3, int nums3Size, int* nums4, int nums4Size) {
struct hashTable* ht = NULL;
for (int i = 0; i < nums1Size; i++) {
for (int j = 0; j < nums2Size; j++) {
int ikey = nums1[i] + nums2[j];
insertOrUpdateHashTable(&ht, ikey, 1);
}
}
int res = 0;
for (int i = 0; i < nums3Size; i++) {
for (int j = 0; j < nums4Size; j++) {
int ikey = -nums3[i] - nums4[j];
struct hashTable *tmp;
HASH_FIND_INT(ht, &ikey, tmp);
if (tmp != NULL) {
res += tmp->val;
}
}
}
freeHashTable(ht); // 释放哈希表中的所有元素
return res;
}
// 示例
int main() {
int nums1[] = {1, 2};
int nums2[] = {-2, -1};
int nums3[] = {-1, 2};
int nums4[] = {0, 2};
int result = fourSumCount(nums1, 2, nums2, 2, nums3, 2, nums4, 2);
printf("%d\n", result);
return 0;
}
key
、val
和 UT_hash_handle
的结构体 hashTable
。freeHashTable
函数使用 HASH_ITER
宏遍历哈希表,并释放每个元素。insertOrUpdateHashTable
函数用于在哈希表中插入新元素或更新现有元素的值。fourSumCount
函数实现了主要的算法逻辑,使用哈希表来统计和为零的四元组数量。main
函数提供了一个示例,调用 fourSumCount
函数并打印结果。数组中所有不重复的三元组,使得三数之和为零。使用 qsort
函数对数组进行排序,并使用双指针技术来寻找符合条件的三元组。
核心部分是排序、双指针搜索和去重。
易错点包括:
难点在于优化搜索过程以提高效率,例如通过提前终止搜索和跳过重复元素。
亮点是使用了两个优化条件来减少不必要的搜索:
int cmp(const void* a, const void* b) {
// 比较函数,用于 qsort,按升序排列
return *(int*)a - *(int*)b;
}
int** threeSum(int* nums, int n, int* returnSize, int** returnColumnSizes) {
// 对数组进行排序
qsort(nums, n, sizeof(int), cmp);
// 动态分配存储所有可能三元组的空间
int** ans = malloc(n * n * sizeof(int*));
// 动态分配存储所有三元组列大小的空间
*returnColumnSizes = malloc(n * n * sizeof(int));
int m = 0; // 用于跟踪结果数组中的三元组数量
for (int i = 0; i < n - 2; i++) {
int x = nums[i];
// 跳过重复的数字以避免重复的三元组
if (i > 0 && x == nums[i - 1]) continue;
// 如果当前元素加上后面两个元素的和大于0,则无需继续搜索
if (x + nums[i + 1] + nums[i + 2] > 0) break;
// 如果当前元素加上数组最后两个元素的和小于0,则无需继续搜索
if (x + nums[n - 2] + nums[n - 1] < 0) continue;
int j = i + 1, k = n - 1;
while (j < k) {
int s = x + nums[j] + nums[k];
// 如果三数之和大于0,移动右指针
if (s > 0) {
k--;
} else if (s < 0) {
// 如果三数之和小于0,移动左指针
j++;
} else {
// 找到三数之和为0的三元组
int* tuple = malloc(3 * sizeof(int));
tuple[0] = x;
tuple[1] = nums[j];
tuple[2] = nums[k];
ans[m] = tuple;
(*returnColumnSizes)[m++] = 3;
// 跳过重复的数字
for (j++; j < k && nums[j] == nums[j - 1]; j++);
for (k--; k > j && nums[k] == nums[k + 1]; k--);
}
}
}
*returnSize = m; // 设置返回的三元组数量
return ans; // 返回结果数组
}
cmp
函数:用于 qsort
的比较函数,按升序排列数组元素。threeSum
函数:实现三数之和的主要逻辑,包括排序、双指针搜索、去重和结果存储。threeSum
函数中,通过 m
变量跟踪结果数组中的三元组数量,通过 *returnSize
和 *returnColumnSizes
向调用者返回结果数组的大小和列大小信息。malloc
动态分配内存来存储结果数组和列大小数组,确保有足够的空间存储所有可能的三元组。for
循环和 while
循环结合双指针技术来寻找所有符合条件的三元组,并在找到后进行去重处理。找出数组中所有唯一的四元组,使得四数之和等于给定的目标值 target
。该程序使用快速排序 quickSort
对数组进行排序,并使用四层嵌套循环结合剪枝和去重技术来寻找符合条件的四元组。
核心部分是排序、四层循环的嵌套遍历、剪枝优化和去重。易错点包括:
难点在于优化遍历过程以提高效率,例如通过剪枝和去重。亮点是使用了快速排序和剪枝条件来减少不必要的搜索:
/**
* 交换两个整数的值
*/
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
/**
* 快速排序的分区函数
*/
int partition(int* nums, int low, int high) {
int pivot = nums[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (nums[j] <= pivot) {
i++;
swap(&nums[i], &nums[j]);
}
}
swap(&nums[i + 1], &nums[high]);
return i + 1;
}
/**
* 快速排序函数
*/
void quickSort(int* nums, int low, int high) {
if (low < high) {
int PI = partition(nums, low, high);
quickSort(nums, low, PI - 1);
quickSort(nums, PI + 1, high);
}
}
/**
* 主函数,找出所有和为 target 的四元组
*/
int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes) {
int i = 0, j = 0, k = 0, z = 0, m = 0;
int** Nums = (int**)malloc(sizeof(int*) * 1001); // 存储结果的数组
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * 1001); // 存储每组的大小
if (numsSize < 4) return Nums;
quickSort(nums, 0, numsSize - 1); // 对数组进行快速排序
for (int i = 0; i < numsSize - 3; i++) { // 第一层循环
if (i > 0 && nums[i] == nums[i - 1]) {
continue; // 去重
}
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break; // 剪枝
}
if ((long) nums[i] + nums[numsSize - 3] + nums[numsSize - 2] + nums[numsSize - 1] < target) {
continue; // 剪枝
}
for (int j = i + 1; j < numsSize - 2; j++) { // 第二层循环
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue; // 去重
}
if ((long)nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break; // 剪枝
}
if ((long)nums[i] + nums[j] + nums[numsSize - 2] + nums[numsSize - 1] < target) {
continue; // 剪枝
}
int k = j + 1, z = numsSize - 1;
while (k < z) { // 双指针
long sum = (long)nums[i] + nums[j] + nums[k] + nums[z];
if (sum == target) {
(*returnColumnSizes)[m] = 4;
Nums[m] = (int*)malloc(sizeof(int) * 4);
Nums[m][0] = nums[i];
Nums[m][1] = nums[j];
Nums[m][2] = nums[k];
Nums[m][3] = nums[z];
m++;
while (k < z && nums[k] == nums[k + 1]) {
k++;
}
k++;
while (k < z && nums[z] == nums[z - 1]) {
z--;
}
z--;
} else if (sum < target) {
k++;
} else {
z--;
}
}
}
}
if (Nums == NULL) {
*returnSize = 0;
**returnColumnSizes = 0;
return Nums;
}
*returnSize = m;
return Nums; // 返回结果数组
}
swap
函数:用于交换两个整数的值。partition
函数:快速排序的分区函数,选择一个枢轴元素并将数组分为两部分。quickSort
函数:快速排序函数,递归地对数组进行排序。fourSum
函数:实现四数之和的主要逻辑,包括排序、四层循环的嵌套遍历、剪枝优化和去重。fourSum
函数中,通过 m
变量跟踪结果数组中的四元组数量,通过 *returnSize
和 *returnColumnSizes
向调用者返回结果数组的大小和列大小信息。malloc
动态分配内存来存储结果数组和列大小数组,确保有足够的空间存储所有可能的四元组。for
循环和 while
循环结合双指针技术来寻找所有符合条件的四元组,并在找到后进行去重处理。