代码随想录训练营-5day:哈希表

一、 哈希表理论基础 

根据代码随想录(代码随想录)的基础理论,需要清楚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;
}

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值