力扣题号:18. 四数之和
一、题目描述
给你一个由 n
个整数组成的数组 nums
,和一个目标值 target
。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
二、示例
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]
三、求解思路
四、代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 99999999
void PrintArr(int* A, int n);
int cmp(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
// 自定义四元组
typedef struct fourarray {
int** arr = NULL;
int size = 0;
}FourArray;
// set元素
typedef struct set {
int key;
}SetNode;
// 自定义哈希set
typedef struct hash_set {
SetNode arr[1005];
}HashSet;
// 映射函数
void voidreflection(HashSet& H, int n) {
int n1 = n % 1000;
if (n1 < 0) {
n1 = -n1;
}
while (H.arr[n1].key != MAX && H.arr[n1].key != n) {
n1++;
n1 %= 1000;
}
if (H.arr[n1].key == n) { // 元素重复
return;
}
H.arr[n1].key = n;
}
// 查找哈希表中是否有该元素
int find(HashSet& H, int n) {
int n1 = n % 1000;
if (n1 < 0) {
n1 = -n1;
}
while (H.arr[n1].key != n && H.arr[n1].key != MAX) {
n1++;
n1 %= 1000;
}
if (H.arr[n1].key == n) {
return 1;
}
return 0;
}
// 哈希表中删除元素
void erase(HashSet& H, int n) {
int n1 = n % 1000;
if (n1 < 0) {
n1 = -n1;
}
while (H.arr[n1].key != n && H.arr[n1].key != MAX) {
n1++;
n1 %= 1000;
}
if (H.arr[n1].key == n) {
H.arr[n1].key = MAX;
return;
}
return;
}
// 四数之和_哈希法
void solution_hash(int* arr, int n, FourArray& result, int target) {
// 先对数组进行排序
qsort(arr, n, sizeof(int), cmp);
PrintArr(arr, n);
// 定义哈希set
HashSet set;
for (int i = 0; i < 1005; i++) {
set.arr[i].key = MAX;
}
result.size = 0;
// 找出a + b + c = 0
// a = nums[i], b = nums[j], c = -(a + b)
for (int k = 0; k < n; k++) {
// 一级剪枝
if (arr[k] > target && arr[k] >= 0) {
break;
}
// 一级去重
if (k > 0 && arr[k] == arr[k - 1]) {
continue;
}
for (int i = k+1; i < n; i++) {
// 二级剪枝
if (arr[i]+arr[k] > target && arr[i]+arr[k] >= 0) {
break;
}
// 二级去重
if (i > k+1 && arr[i] == arr[i - 1]) {
continue;
}
// 重新初始化set
for (int i = 0; i < 1005; i++) {
set.arr[i].key = MAX;
}
for (int j = i + 1; j < n; j++) {
if (j > i + 2
&& arr[j] == arr[j - 1]
&& arr[j - 1] == arr[j - 2]) { // 四元组元素c去重
continue;
}
int c = target - (arr[k] + arr[i] + arr[j]);
if (find(set, c)) {
int* res = (int*)malloc(4 * sizeof(int));
res[0] = arr[k]; res[1] = arr[i]; res[2] = arr[j]; res[3] = c;
if (result.size == 0) {
result.arr = (int**)malloc(sizeof(int*));
result.arr[0] = res;
result.size++;
}
else {
result.arr = (int**)realloc(result.arr, sizeof(int*) * (result.size + 1));
result.arr[result.size] = res;
result.size++;
}
erase(set, c);// 四元组元素d去重
}
else {
voidreflection(set, arr[j]);
}
}
}
}
}
// 四数之和_双指针法
void solution_doublepoint(int* arr, int n, FourArray& result, int target) {
// 先对数组进行排序
qsort(arr, n, sizeof(int), cmp);
result.size = 0;
PrintArr(arr, n);
for (int k = 0; k < n; k++) {
// 剪枝处理
if (arr[k] > target && arr[k] >= 0) {
break;
}
// 对arr[k]去重
if (k > 0 && arr[k] == arr[k - 1]) {
continue;
}
for (int i = k+1; i < n; i++) {
// 二级剪枝处理
if (arr[i]+arr[k] > target && arr[i]+arr[k] >= 0) {
break;
}
// 对arr[i]二级去重
if (i > k+1 && arr[i] == arr[i - 1]) {
continue;
}
int left = i + 1;
int right = n - 1;
while (left < right) {
if (arr[k]+ arr[i] + arr[left] + arr[right] > target) {
right--;
}
else if (arr[k]+ arr[i] + arr[left] + arr[right] < target) {
left++;
}
else { // 保存到四元组
int* res = (int*)malloc(4 * sizeof(int));
res[0] = arr[k]; res[1] = arr[i]; res[2] = arr[left]; res[3] = arr[right];
if (result.size == 0) {
result.arr = (int**)malloc(sizeof(int*));
result.arr[0] = res;
result.size++;
}
else {
result.arr = (int**)realloc(result.arr, sizeof(int*) * (result.size + 1));
result.arr[result.size] = res;
result.size++;
}
// 去重逻辑应该放在找到一个四元组之后,对c 和 d去重
while (left < right && arr[right] == arr[right - 1]) {
right--;
}
while (left < right && arr[left] == arr[left + 1]) {
left++;
}
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return;
}
// 打印数组
void PrintArr(int* A, int n) {
for (int i = 0; i < n; i++) {
printf("%4d ", A[i]);
}
printf("\n");
for (int i = 0; i < n; i++) {
printf("-----");
}
printf("\n");
}
// 打印四元组
void PrintFourArray(FourArray& A) {
for (int i = 0; i < A.size; i++) {
for (int j = 0; j < 4; j++) {
printf("%4d ", A.arr[i][j]);
}
printf("\n");
}
printf("\n");
}
int main() {
int a1[] = { 0,52,98,-99,15,-52,52,52,52,52,52,105, 1, -106,400,395,84,77,89,512,45,88,9,1034 };
int n1 = sizeof(a1) / sizeof(int);
FourArray res;
printf("初始数组:\n");
PrintArr(a1, n1);
printf("\n");
// 哈希表法
solution_hash(a1, n1, res,104);
printf("\n哈希表法:target = %4d \n\n", 104);
PrintFourArray(res);
// 双指针法
FourArray res2;
solution_doublepoint(a1, n1, res2,104);
printf("\n双指针法:target = %4d \n\n", 104);
PrintFourArray(res2);
return 0;
}