前言
思路及算法思维,指路 代码随想录。
题目来自 LeetCode。
day 7,周二,继续继续~
题目详情
[454] 四数之和Ⅱ
题目描述
解题思路
前提:四个证书组 + 所求符合条件的元组个数
思路:哈希,就四数之和,可以简化为(A+B)+C+D == 0的形式(三数之和)。
重点:构造哈希结构,不仅需要保存key,还需要保存key的数量。
代码实现
C语言
使用UT_hash库接口
typedef struct hashTable{
int val;
int cnt;
UT_hash_handle hh;
};
void TwoSum(int* numsA, int numsASize, int* numsB, int numsBSize, struct hashTable **hash){
for (int i = 0; i < numsASize; i++)
{
for (int j = 0; j < numsBSize; j++)
{
int sumAB = numsA[i] + numsB[j];
struct hashTable *tmp;
HASH_FIND_INT(*hash, &sumAB, tmp);
if (tmp == NULL)
{
struct hashTable *tmp = (struct hashTable *)malloc(sizeof(struct hashTable));
tmp->val = sumAB;
tmp->cnt = 1;
HASH_ADD_INT(*hash, val, tmp);
}
else
{
tmp->cnt++;
}
}
}
return ;
}
int fourSumCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* nums3, int nums3Size, int* nums4, int nums4Size){
struct hashTable *hashTable = NULL;
TwoSum(nums1, nums1Size, nums2, nums2Size, &hashTable);
int count = 0;
for (int i = 0; i < nums3Size; i++)
{
for (int j = 0; j < nums4Size; j++)
{
int val = 0 - nums3[i] - nums4[j];
struct hashTable *tmp;
HASH_FIND_INT(hashTable, &val, tmp);
if (tmp != NULL)
{
count += tmp->cnt;
}
else
{
}
}
}
return count;
}
自己实现哈希接口
#define HASH_TABLE_LEN 256
typedef struct Node {
int val;
int cnt;
struct Node *next;
}NODE, *HashTable;
void initHashTable(HashTable *hash)
{
for (int i = 0; i < HASH_TABLE_LEN; i++)
{
struct Node *head = (struct Node *)malloc(sizeof(struct Node));
memset(head, 0, sizeof(struct Node));
hash[i] = head;
}
return ;
}
void insertHashTable(HashTable *hash, int val)
{
int index = val < 0 ? (-val) % HASH_TABLE_LEN : val % HASH_TABLE_LEN;
struct Node *last = hash[index]; //虚拟头节点
while (last->next)
{
if (last->next->val == val)
{
last->next->cnt++;
return;
}
last = last->next;
}
struct Node *new = (struct Node *)malloc(sizeof(struct Node));
new->val = val;
new->cnt = 1;
new->next = NULL;
last->next = new;
return ;
}
int foundHashTable(HashTable *hash, int val)
{
int index = val < 0 ? (-val) % HASH_TABLE_LEN : val % HASH_TABLE_LEN;
struct Node *cur = hash[index];
while (cur->next)
{
if (cur->next->val == val)
{
return cur->next->cnt;
}
cur = cur->next;
}
return 0;
}
void freeHashTable(HashTable *hash)
{
for (int i = 0; i < HASH_TABLE_LEN; i++)
{
struct Node *cur = hash[i];
while (cur)
{
struct Node *next = cur->next;
free(cur);
cur = next;
}
}
return ;
}
void TwoSum(int* numsA, int numsASize, int* numsB, int numsBSize, HashTable *hash){
for (int i = 0; i < numsASize; i++)
{
for (int j = 0; j < numsBSize; j++)
{
int sumAB = numsA[i] + numsB[j];
insertHashTable(hash, sumAB);
}
}
return ;
}
int fourSumCount(int* nums1, int nums1Size, int* nums2, int nums2Size, int* nums3, int nums3Size, int* nums4, int nums4Size){
HashTable hashTable[HASH_TABLE_LEN];
initHashTable(hashTable);
TwoSum(nums1, nums1Size, nums2, nums2Size, hashTable);
int count = 0;
for (int i = 0; i < nums3Size; i++)
{
for (int j = 0; j < nums4Size; j++)
{
count += foundHashTable(hashTable, (0 - nums3[i] - nums4[j]));
}
}
//freeHashTable(hashTable);
return count;
}
[383] 赎金信
题目描述
解题思路
前提:ransomNote中的字母是不是完全由magazine中字母组成
思路:统计magazine中字母出现的次数,看是否完全包含ramsomNote字母的次数。
重点:哈希结构,由于均有小写字母组成,使用数组实现即可。
代码实现
C语言
bool canConstruct(char* ransomNote, char* magazine) {
int hash[26] = {0};
if (strlen(magazine) < strlen(ransomNote))
{
return false;
}
for (int i = 0; i < strlen(magazine); i++)
{
hash[magazine[i] - 'a']++;
}
for (int j = 0; j < strlen(ransomNote); j++)
{
if (hash[ransomNote[j] - 'a'] > 0)
{
hash[ransomNote[j] - 'a']--;
}
else
{
return false;
}
}
return true;
}
[15] 三数之和
题目描述
解题思路
前提:三元组不重复
思路:三元组不重复,必定要对nums数组去重,需要三个指针,left、middle、right,遍历left指针,在left指针固定的情况下,middle在(left, right)范围遍历,不断收缩middle、right指针,直至重合,遍历完成。
重点:去重操作的判断,当指针移动时,均需要判重过滤。
代码实现
C语言
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int cmp(const void *p1, const void *p2)
{
return (int)(*(int *)p1 > *(int *)p2);
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
if (numsSize < 3)
{
*returnSize = 0;
return NULL;
}
// 排序,方便去重
qsort(nums, numsSize, sizeof(int), cmp);
*returnSize = 0;
int mallocSize = numsSize;
int **ret = (int **)malloc(sizeof(int *) * mallocSize);
*returnColumnSizes = (int *)malloc(sizeof(int) * mallocSize);
// 遍历left
for (int left = 0; left < numsSize - 2; left++)
{
// 初始化middle、right
int middle = left + 1;
int right = numsSize - 1;
// 过滤重复left
if ((left > 0) && (nums[left] == nums[left - 1]))
{
continue;
}
// 寻找符合条件的middle和right
while (middle < right)
{
int sum = nums[left] + nums[middle] + nums[right];
if (sum > 0)
{
right--;
continue;
}
else if (sum < 0)
{
middle++;
continue;
}
// 找到符合条件的middle和right
// 返回空间不足时重新分配空间
if (mallocSize <= *returnSize)
{
mallocSize = mallocSize * 2;
ret = (int **)realloc(ret, sizeof(int *) * mallocSize);
*returnColumnSizes = (int *)realloc(*returnColumnSizes, sizeof(int) * mallocSize);
}
ret[*returnSize] = (int *)malloc(sizeof(int) * 3);
ret[*returnSize][0] = nums[left];
ret[*returnSize][1] = nums[middle];
ret[*returnSize][2] = nums[right];
(*returnColumnSizes)[*returnSize] = 3;
(*returnSize)++;
//对已找到的middle和right移位去重
int tmp = nums[middle];
while ((middle < right) && (nums[middle] == tmp))
{
middle++;
}
tmp = nums[right];
while ((middle < right) && (nums[right] == tmp))
{
right--;
}
}
}
return ret;
}
[18] 四数之和
题目描述
解题思路
前提:四元组不重复
思路:四元组不重复,必定要对nums数组去重,需要四个指针,loc1、loc2、loc3、loc4,遍历loc1和loc2指针,在loc1和loc2指针固定的情况下,loc3在(loc2, loc4)范围遍历,不断收缩loc3、4oc3指针,直至重合,遍历完成。
重点:去重操作的判断,当指针移动时,均需要判重过滤。
代码实现
C语言
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int compare(const void *p1, const void *p2)
{
return (*(int *)p1 > *(int *)p2);
}
int** fourSum(int* nums, int numsSize, int target, int* returnSize, int** returnColumnSizes) {
int mallocSize = numsSize;
*returnSize = 0;
//排除异常情况
if (numsSize < 4)
{
return NULL;
}
// 排序,以便去重
qsort(nums, numsSize, sizeof(int), compare);
int **ret = (int **) malloc(sizeof(int *) * mallocSize);
*returnColumnSizes = (int *)malloc(sizeof(int) * mallocSize);
// 寻找四元组
// 遍历loc1
for (int loc1 = 0; loc1 < numsSize; loc1++)
{
// 去重
if ((loc1 > 0) && (nums[loc1] == nums[loc1 - 1]))
{
continue;
}
// 遍历loc2
for (int loc2 = (loc1 + 1); loc2 < numsSize; loc2++)
{
// 去重
if((loc2 > (loc1 + 1)) && (nums[loc2] == nums[loc2 - 1]))
{
continue;
}
// 初始化loc3 和loc4
int loc3 = loc2 + 1;
int loc4 = numsSize - 1;
// 寻找符合条件的loc3和loc4
while (loc3 < loc4)
{
long sum = (long)nums[loc1] + (long)nums[loc2] + (long)nums[loc3] + (long)nums[loc4];
if (sum > target)
{
loc4--;
continue;
}
else if (sum < target)
{
loc3++;
continue;
}
// 找到符合条件的loc3和loc4
// 返回空间不足时重新分配空间
if (mallocSize <= *returnSize)
{
mallocSize *= 2;
ret = (int **)realloc(ret, sizeof(int *) * mallocSize);
*returnColumnSizes = (int *)realloc(*returnColumnSizes, sizeof(int) * mallocSize);
}
// 赋值四元组
ret[*returnSize] = (int *)malloc(sizeof(int) * 4);
ret[*returnSize][0] = nums[loc1];
ret[*returnSize][1] = nums[loc2];
ret[*returnSize][2] = nums[loc3];
ret[*returnSize][3] = nums[loc4];
(*returnColumnSizes)[*returnSize] = 4;
(*returnSize)++;
// 对已找到的loc3和loc4去重
int tmp = nums[loc3];
while ((loc3 < loc4) && nums[loc3] == tmp)
{
loc3++;
}
tmp = nums[loc4];
while ((loc3 < loc4) && nums[loc4] == tmp)
{
loc4--;
}
}
}
}
return ret;
}
今日收获
- 哈希的实现:数组、自实现hash库、UT_hash库的使用。
- 四数之和、四数之和Ⅱ的区别;三数之和、四数之和对于去重的判断:要跟之前遍历过得元素判重,以便遗漏符合条件的组合。