************
思路
一、暴力,四重循环,时间复杂度 O(n⁴)(我没试)
二、(我愿称之)时间复杂度拆分法,将四个嵌套循环拆成两个双层嵌套循环,这样时间复杂度降低至O(n ²)。
将前两个数组的所有和存储在map中,key值记录两数和,val记录出现次数,之后再用嵌套循环计算后两数和,查找map中是否出现过需要的另外两数之和
代码 时间复杂度拆分法(map实现)
typedef struct map {
int key;
int val;
struct map* next;
} mapnode;
#define MapSize 100
mapnode* Map[MapSize];
// 哈希函数
int hash(int key) { return (key % MapSize + MapSize) % MapSize; }
// 创建新的map节点
mapnode* create(int key) {
mapnode* pair = (mapnode*)malloc(sizeof(mapnode));
pair->key = key;
pair->val = 1;
pair->next = NULL;
return pair;
}
// 将新的和添加到map
void Mapadd(int key) {
int index = hash(key);
mapnode* p = Map[index];
mapnode* aim = NULL;
while (p != NULL) // 查看是否已经记录过当前key
{
if (p->key == key) {
p->val++;
aim = p;
break;
}
p = p->next;
}
if (aim == NULL) // 若没有记录过,创建新的
{
aim = create(key);
aim->next = Map[index];
Map[index] = aim;
}
return;
}
int search(int key) {
int index = hash(key);
mapnode* p = Map[index];
while (p != NULL) {
if (p->key == key) {
return p->val;
}
p = p->next;
}
return 0;
}
void destory(void) {
for (int i = 0; i < MapSize; i++) {
if (Map[i] != NULL) {
mapnode* pair = Map[i];
while (pair != NULL) {
mapnode* temp = pair;
pair = pair->next;
free(temp);
}
}
}
return;
}
int fourSumCount(int* nums1, int nums1Size, int* nums2, int nums2Size,
int* nums3, int nums3Size, int* nums4, int nums4Size) {
memset(Map, 0, sizeof(Map));
for (int i = 0; i < nums1Size; i++) {
for (int j = 0; j < nums2Size; j++) {
Mapadd(nums1[i] + nums2[j]);
}
}
int sum = 0;
for (int i = 0; i < nums3Size; i++) {
for (int j = 0; j < nums4Size; j++) {
int key = 0 - nums4[j] - nums3[i];
sum += search(key);
}
}
destory();
return sum;
}
********
思路
哈希表记录 数组magazine中出现过的字母的次数(遍历),再用 数组ransomNote中出现过的字母减去记录好的次数(再遍历),出现负数代表不能,返回false结束,若顺利遍历结束,返回true。
代码
bool canConstruct(char* ransomNote, char* magazine) {
int a[30] = {0};
for (int i = 0; i < strlen(magazine); i++) {
a[magazine[i] % 26]++;
}
for (int i = 0; i < strlen(ransomNote); i++) {
if (a[ransomNote[i] % 26] == 0) {
return false;
} else {
a[ransomNote[i] % 26]--;
}
}
return true;
}
********
难点
1、二维数组malloc易出错,初始化易出错
2、去重思维不好把握,细节太多
思路
双指针,先排序,for循环遍历每个元素,固定a(注意去重,判断当前元素与前一项是否相等),用两个指针left(去重)和right(去重)循环a右边元素,根据三数大小进行对left和right的调整
left和right的去重是在找到符合题目要求的元素之后进行,避免越过答案。
代码
#define MaxSize 20000
int cmp(const void* a, const void* b) { return (*(int*)a - *(int*)b); }
int** threeSum(int* nums, int numsSize, int* returnSize,
int** returnColumnSizes) {
qsort(nums, numsSize, sizeof(int), cmp);
int** a = (int**)malloc(sizeof(int*) * MaxSize);
for (int i = 0; i < MaxSize; i++) {
a[i] = (int*)malloc(sizeof(int) * 3);
}
int left, right;
*returnSize = 0;
for (int i = 0; i < numsSize - 2; i++) {
if ((i != 0 && nums[i] == nums[i - 1]) || nums[i] > 0)
continue;
left = i + 1;
right = numsSize - 1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] == 0) {
a[*returnSize][0] = nums[i];
a[*returnSize][1] = nums[left];
a[*returnSize][2] = nums[right];
(*returnSize)++;
while (left < numsSize - 1 && nums[left] == nums[left + 1]) {
left++;
}
while (right > i + 1 && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
} else if (nums[i] + nums[left] + nums[right] > 0) {
right--;
} else {
left++;
}
}
}
*returnColumnSizes = (int*)malloc(sizeof(int) * (*returnSize));
for (int i = 0; i < (*returnSize); i++) {
(*returnColumnSizes)[i] = 3;
}
return a;
}
********
思路
写过三数之和之后,这题是几乎一样的
易错点
1、left和right去重,while循环之后还要进行一步left++和right--,不然很可能编译不通过
2、i和j剪枝时注意不要单纯判断nums[i]>target,当target是负数时,很容易错过正确答案
代码
#define MaxSize 2000
int cmp(const void* a, const void* b) { return (*(int*)a - *(int*)b); }
int** fourSum(int* nums, int numsSize, int target, int* returnSize,
int** returnColumnSizes) {
int** a = (int**)malloc(sizeof(int*) * MaxSize);
for (int i = 0; i < MaxSize; i++)
a[i] = (int*)malloc(sizeof(int) * 4);
int left, right;
*returnSize = 0;
qsort(nums, numsSize, sizeof(int), cmp);
for (int i = 0; i < numsSize - 3; i++) {
if (nums[i] > target&&target>0)
break;
if (i != 0 && nums[i - 1] == nums[i])
continue;
for (int j = i + 1; j < numsSize - 2; j++) {
if (nums[i] + nums[j] > target&&target>0)
break;
if (j != i + 1 && nums[j] == nums[j - 1])
continue;
left = j + 1;
right = numsSize - 1;
while (left < right) {
long long sum =
(long long)nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
a[*returnSize][0] = nums[i];
a[*returnSize][1] = nums[j];
a[*returnSize][2] = nums[left];
a[*returnSize][3] = nums[right];
(*returnSize)++;
while (left + 1 < numsSize && nums[left] == nums[left + 1])
left++;
while (right - 1 > j && nums[right] == nums[right - 1])
right--;
left++;
right--;
} else if (sum > target) {
right--;
} else if (sum < target) {
left++;
}
}
}
}
*returnColumnSizes = (int*)malloc(sizeof(int) * (*returnSize));
for (int i = 0; i < (*returnSize); i++) {
(*returnColumnSizes)[i] = 4;
}
return a;
}
哈希表总结
头脑风暴:简单数组哈希,链表哈希(处理哈希冲突),哈希函数(处理负数方面),map(哈希进阶版,注意确认key和val的意义),哈希不了(去重要求)就双指针,哈希次数太多(例如四数相加)换思路拆分为两个两个,注意去重操作(分别i,j,left,right都有一些细节)