【代码随想录算法训练营第六天 | 哈希表理论基础 242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和】

哈希表

理论基础

引入

定义

  • 哈希表是根据关键码的值而直接进行访问的数据结构(比如数组)

解决问题

  • 一般是用来快速判断一个元素是否出现在集合里(亦或判断一个元素之前是否出现过)
  • 牺牲空间换时间( 因为我们要使用额外的数组/set/map来存放数据才能实现快速的查找,查找时间效率是O(1) )

哈希函数

  • 功能:将目标映射到哈希表上的索引(key)
  • 实现: 哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

哈希表2

哈希碰撞

  • 目标:解决dataSize(hashCode)大于tableSize的问题,此时会出现多个学生映射到同一索引上的情况。

哈希表3

  • 方法

    • 拉链法:发生冲突的元素被储存在链表中。

    其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

    哈希表4

    • 线性探测法: tableSize一定要大于dataSize,依靠哈希表中的空位解决冲突问题。

    例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息 。

    哈希表5

哈希结构

  • 数组
    • 下标代替索引映射:[i]-value
    • 数据范围较小时使用:O(tableSize) < O(dataSize^2)
  • 集合(set)
    • 存储非重复值(去重遍历查找)
    • 没有索引映射:value in set?
    • 数据范围不定或者范围大,元素少且离散时使用:O(tableSize) > O(dataSize^2)
    • 可以用结构体和数组(动态/静态) 或 链表实现
  • 映射(map)

有效的字母异位词

leetocde

代码随想录

  • 前提

    • 字符串只包含小写字母
  • 要点

    • 定义一个大小为26(字母)的哈希表(数组)record,所有value初始化为0 。
    • s字符串的字母出现一次对应索引的value+1,t字符串的字母出现一次对应索引的value-1,若record最终为0,返回True,否则返回False。

    242.有效的字母异位词

哈希数组

时间复杂度O(n),因为空间大小是常量,所以空间复杂度为O(1)

bool isAnagram(char* s, char* t) {
    int record[26] = {0};
    int sSize = strlen(s);
    int tSize = strlen(t);

    if (sSize != tSize){
        return false;
    }

    for (int i = 0; i < sSize; i++){
        // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
        record[(s[i]-'a')] ++;
        record[(t[i]-'a')] --;
    } 

    for (int i = 0; i < 26; i++){
        if (record[i] != 0){
            return false;
        }
    }

    return true;
}

两个数组的交集

leetcode

代码随想录

  • 前提
    • 输出结果中的每个元素是唯一的(去重)
    • 可以不考虑输出结果的顺序
    • 限制了数值的大小,可以使用数组
  • 要点
    • 用哈希表记录数组1的元素,遍历数组2 时查找哈希表判断此元素是否已存在。
    • 若在哈希表中查找到该元素,结束后将对应的哈希表值置零以去重。

哈希数组

时间复杂度O(m+n),空间复杂度为O(n)

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
    int nums1cntRecord[1000] = {0};
    int lessSize = nums1Size < nums2Size ? nums1Size : nums2Size;
    int *returnNums = calloc(lessSize, sizeof(int));
    int returnIndex = 0;
    int i;

    for (i = 0; i < nums1Size; i++){
        nums1cntRecord[nums1[i]]++;
    }

    for (i = 0; i < nums2Size; i++){
        if (nums1cntRecord[nums2[i]] > 0){
            returnNums[returnIndex] = nums2[i];
            returnIndex++;
            nums1cntRecord[nums2[i]] = 0;
        }
    }

    *returnSize = returnIndex;

    return returnNums;
}

哈希set

自定义set数据结构,太麻烦不用

快乐树

leetcode

代码随想录

  • 前提
    • 正整数
  • 要点
    • 题目说会无限循环代表求和的过程中sum会重复出现。
    • 退出条件:sum重复出现,若此时sum=1则返回True,否则返回False。
    • 方法一:数学不等式缩小sum范围,使用数组哈希查找是否重复出现
    • 方法二:快慢指针循环相遇
/*
标准库数据类型
typedef struct _div_t{
    int quot;
    int rem;
}div_t;

函数div(int numer, int denom),分子number除以分母denom,返回商quot、余数rem。

typedef unsigned char           uint8_t;   
- 8位无符号整数类型
- _t 表示这些数据类型是通过typedef定义的,而不是新的数据类型 
*/
int getsum(int n){
    div_t div_n = {.quot = n};
    int sum = 0;

    while (div_n.quot){
        div_n = div(div_n.quot, 10);
        sum += div_n.rem * div_n.rem;
    }

    return sum;
}

哈希数组

bool isHappy(int n) {
    //1 <= n <= 2^31 - 1 (2^31 = 2147483648)
    // sum = a1^2 + a2^2 + ... ak^2
    // first round:
    // 1 <= k <= 10
    // 1 <= sum <= 1 + 81 * 9 = 730 (25+25 < 81+1)
    // second round:
    // 1 <= k <= 3
    // 1 <= sum <= 36 + 81 * 2 = 198
    // third round:
    // 1 <= sum <= 81 * 2 = 162
    // fourth round:
    // 1 <= sum <= 81 * 2 = 162

    uint8_t sumRecord[163] = {0};
    int sum = getsum(getsum(getsum(n)));

    while (sumRecord[sum] == 0){
        sumRecord[sum] = 1;
        sum = getsum(sum);
    }

    return sum==1;
    
}

快慢指针

bool isHappy(int n) {
    int fast = n;
    int slow = n;

    do{
        fast = getsum(getsum(fast));
        slow = getsum(slow);
    }while (fast != slow);

    return fast==1;
}

两数之和

leetcode

代码随想录

  • 前提
    • 数组中同一个元素在答案里不能重复出现
    • 可以假设只存在一个有效答案
    • 可以按任意顺序返回答案
  • 要点
    • 查找target-num[i]是否在哈希表中
    • 因为范围不定,且需要查找数值和返回数组下标,选择map
    • key存数值,value存下标

哈希map

/* leetcode 支持 ut_hash 函式庫 */
typedef struct {
    int key;
    int value;
    UT_hash_handle hh;
}map;

map *hashMap = NULL;

map* hashMapFind(int key){
    map* tmp = NULL;
    HASH_FIND_INT(hashMap, &key, tmp);
    return tmp;
}

void hashMapAdd(int key, int value){ 
    if (hashMapFind(key) == NULL){
        map* tmp = malloc(sizeof(map));
        tmp -> key = key;
        HASH_ADD_INT(hashMap, key, tmp);
        tmp -> value = value;
    }
}

void hashMapPrint(){
    map* cur = NULL;
    for (cur = hashMap; cur != NULL; cur = cur -> hh.next){
        printf("key:%d, value:%d\n", cur -> key, cur -> value);
    }
}

void hashMapCleanUp(){
    map *cur, *tmp;

    HASH_ITER(hh, hashMap, cur, tmp){
        HASH_DEL(hashMap, cur);
        free(cur);
    }
}

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    map *hashMapRes = NULL;
    int *indexRes = malloc(sizeof(int)*2);
    hashMap = NULL;

    for (int i = 0; i < numsSize; i++){
        hashMapAdd(nums[i], i);
    }

    hashMapPrint();

    for (int i = 0; i < numsSize; i++){
        hashMapRes = hashMapFind(target - nums[i]);
        if (hashMapRes != NULL && hashMapRes -> value != i){
            indexRes[0] = i;
            indexRes[1] = hashMapRes -> value;
            *returnSize = 2;
            return indexRes;
        }
    }
    
    hashMapCleanUp();

    return NULL;
}
  • 32
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值