一、 哈希表理论基础
根据代码随想录(代码随想录)的基础理论,需要清楚hash冲突,以及如何解决;另外C++里面的hash函数的数据结构以及特点;最重要一句话:当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法!!!
另外,C语言内部没有hash函数,需要自己实现,通过阅读参考其他的博文(该文章的链接:C语言哈希表用法_hash_find_int-CSDN博客),需要注意的key value的数据类型。
二、 242.有效的字母异位词
其实这个题目就是找A数组内部元素是否和B数组内部元素重叠,这里就印证一个思想:一个元素是否出现在集合内,那么需要考虑hash法。
字母的元素有26个,因此可以用一个26个元素的数组来表示hash set,当遍历A数组时候,记录一个元素出现的次数,如果两个数组元素重叠,那么在B元素内部遍历,减掉相对应的次数,最终肯定的是hash set内部元素肯定是0值,即次数对应上了,否则肯定有元素没匹配上。
bool isAnagram(char* s, char* t) {
//26个字母
int num[26] = {0};//init 字母出现次数0
for(int i =0; i < strlen(s); i++)
{
num[s[i] - 'a']++;
}
/**如果s字符串的字符出现次数是与t字符串匹配,那么num数组会回到0**/
for(int i = 0; i< strlen(t); i++)
{
num[t[i] - 'a']--;
}
for(int i = 0; i < 26; i++)
{
if(num[i] != 0)
{
return false;
}
}
return true;
}
三、 349. 两个数组的交集
C++有hash set模板函数,比较方便使用,C语言必须自己实现(可参考以下代码:力扣(LeetCode))
根据题目,其实数据限制了在1000之内,所以用数组来模拟hash set也是可以的,具体的思路是创建一个1000大小的数组,并初始化0,然后同样创建大小一样的数组作为result数组;开始遍历A数组,把数组元素出现次数,记录下来,(因为只需要知道存在或者不存在,可以赋值为1,不需要累加次数),然后遍历B数组,当hash set内有这样元素时候,把结果存入result数组,并把hash set内部数组清掉,避免下次重复写入,遍历完成后,malloc一个buff,作为函数返回;
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
#define MAXSIZE 769/* 选取一个质数即可 */
typedef struct Node
{
int elem;
struct Node *next; //保存链表表头
} List;
typedef struct
{
List *hashHead[MAXSIZE];//定义哈希数组的大小
} MyHashSet;
bool isInHash(List *list, int key)
{
List *nodeIt = list;
//通过链表下遍历
while (nodeIt != NULL)
{
if (nodeIt->elem == key)
{
return true;
}
nodeIt = nodeIt->next;
}
return false;
}
MyHashSet* myHashSetCreate()
{
int i;
MyHashSet* newHash= (MyHashSet* )malloc(sizeof(MyHashSet));
/* 对链表的头结点赋初值 */
for (i = 0; i < MAXSIZE; i++)
{
newHash->hashHead[i] = NULL;
}
return newHash;
}
void myHashSetAdd(MyHashSet* obj, int key)
{
//插入在Head处
if(isInHash(obj->hashHead[key%MAXSIZE],key))
{
//不用添加了
return;
}
List *newNode = (List*)malloc(sizeof(List));
newNode->elem = key;
newNode->next = NULL;
if(obj->hashHead[key%MAXSIZE] != NULL)
{
//当前头链表不为空,则需要将后续的链表接上
//需要主要这里表头也代表一个数据的值
newNode->next = obj->hashHead[key%MAXSIZE];
}
//修改头链表
obj->hashHead[key%MAXSIZE] = newNode;
}
void myHashSetRemove(MyHashSet* obj, int key)
{
List *preIt = NULL;
List *curIt = obj->hashHead[key%MAXSIZE];
//通过链表下遍历
while (curIt != NULL)
{
if (curIt->elem == key)
{
break;
}
preIt = curIt;
curIt = curIt->next;
}
if(curIt == NULL)
{
//没有找到
return;
}
//找到了
if(preIt == NULL)
{
//等于表头
obj->hashHead[key%MAXSIZE] = curIt->next;
}
else
{
preIt->next = curIt->next;
}
free(curIt);
}
bool myHashSetContains(MyHashSet* obj, int key)
{
return isInHash(obj->hashHead[key%MAXSIZE],key);
}
void myHashSetFree(MyHashSet* obj)
{
int i;
List *freeIt;
List *curIt;
for (i = 0; i < MAXSIZE; i++)
{
if(obj->hashHead[i] != NULL)
{
freeIt = NULL;
curIt = obj->hashHead[i];
while(curIt != NULL)
{
freeIt = curIt;
curIt= curIt->next;
free(freeIt);
}
obj->hashHead[i]= NULL;
}
}
free(obj);
}
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize)
{
MyHashSet* haseSet = myHashSetCreate();
//对数组1建立哈希集合
int i= 0;
for(i = 0; i < nums1Size;i++)
{
myHashSetAdd(haseSet,nums1[i]);
}
//申请一个最大的数组用来存放
int *returnNums = (int *)malloc(sizeof(int) *(nums1Size +nums2Size ) );
//通过数组1建立的 哈希表 来查找 数组2
int interNum = 0;
for(i = 0; i < nums2Size;i++)
{
if(myHashSetContains(haseSet,nums2[i]))
{
//找到的直接输出
returnNums[interNum] = nums2[i];
interNum++;
//从哈希表中删除
myHashSetRemove(haseSet,nums2[i]);
}
}
*returnSize = interNum;
myHashSetFree(haseSet);
return returnNums;
}
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
//nums[i]有限制,值最大是1000,因此可以用数组来存
int timenums[1000] = {0};
int result[1000] = {0};//.这个在栈区,函数返回后会释放,数据保存不了,必须用malloc
//*returnSize = 0;
int idex = 0;
for(int i = 0; i < nums1Size; i++)
{
timenums[nums1[i]] = 1;
}
for(int i = 0; i < nums2Size; i++)
{
if(timenums[nums2[i]] == 1)
{
result[idex++] = nums2[i];
timenums[nums2[i]]--;//已经保存了,需要减掉
}
}
*returnSize = idex;
int* result_ptr = (int*)malloc(sizeof(int) * idex);//malloc在堆区
memcpy(result_ptr, result, idex * sizeof(int));
return result_ptr;
}
四、 202. 快乐数
快乐数,需要理解这句话:“重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1”,意思是可能会有重复的值出现,导致陷入循环,这样一直跳不出来,肯定得不到1,因此需要一个hash set来保存遍历过程数字是否重复。
//理解无限循环 始终不会到1,这个点说明数据会有重复,需要判断是不是在里面
int trans(int n)
{
int sum = 0;
while(n)
{
sum += (n % 10) * (n % 10);
n = n/10;
}
return sum;
}
bool isHappy(int n) {
//2^31次方最多10位数,最极限的值就10个9, 10位 * 9^2 = 810
int* hash = (int*)calloc(sizeof(int), 820);
int sum = trans(n);
while(sum != 1)
{
if(hash[sum] == 1)
{
return false;
}
else
{
hash[sum] = 1;
}
sum = trans(sum);
}
return true;
}
五、 1. 两数之和
这个题用暴力解法,两层for循环遍历,找到满足条件的直接返回,否则一直遍历。
如果使用hash法,思路如下:首先创建一个hash map,从indx=0开始,如果target - num[indx]不在,那么就加入map,如果找到,malloc 一个buff返回,否则一直遍历到数组结束。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
// int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
// int* returnsum = (int*)calloc(sizeof(int), 2);
//
// for(int i = 0; i < numsSize; i++)
// {
// returnsum[0] = i;
// for(int j = i+1; j < numsSize; j++)
// {
// if(target == (nums[j] + nums[i]))
// {
// returnsum[1] = j;
// *returnSize = 2;
// return returnsum;
// }
// }
// }
// *returnSize = 0;
// return returnsum;
// }
struct hashTable {
int key;
int val;
UT_hash_handle hh;
};
struct hashTable* hashtable;
struct hashTable* find(int ikey) {
struct hashTable* tmp;
HASH_FIND_INT(hashtable, &ikey, tmp);
return tmp;
}
void insert(int ikey, int ival) {
struct hashTable* it = find(ikey);
if (it == NULL) {
struct hashTable* tmp = malloc(sizeof(struct hashTable));
tmp->key = ikey, tmp->val = ival;
HASH_ADD_INT(hashtable, key, tmp);
} else {
it->val = ival;
}
}
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
hashtable = NULL;
for (int i = 0; i < numsSize; i++) {
struct hashTable* it = find(target - nums[i]);
if (it != NULL) {
int* ret = malloc(sizeof(int) * 2);
ret[0] = it->val, ret[1] = i;
*returnSize = 2;
return ret;
}
insert(nums[i], i);//key是num的value indx作为hash的value
}
*returnSize = 0;
return NULL;
}