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

LeetCode 242.有效的字母异位词

题目链接:242.有效的字母异位词
文章讲解:代码随想录#242.有效的字母异位词
视频讲解:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词

题目描述

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例1

输入: s = “anagram”, t = “nagaram”
输出: true

示例2

输入: s = “rat”, t = “car”
输出: false

提示

  • 1 <= s.length, t.length <= 5 * 104
  • s 和 t 仅包含小写字母

思路

这是一道比较典型的使用哈希表的简单算法题。
因为要计算字符串中每个字符出现的次数,很容易想到使用哈希表太记录次数。
一般元素个数有限,会使用数组作为哈希表。
题目中已知字符串中只包含小写字母,所以可以构建一个数组letter[26],下标为字符c-‘a’,元素的值为字符c出现的次数。
首先遍历字符串s,字符c出现后,值加1,然后再遍历字符串t,字符c出现后,值减1。
最后重新遍历数组letter,如果有元素非零,说明s和t互为字母异位词。

参考代码

bool isAnagram(char* s, char* t) {
    int letter[26] = {0};

    for (int i = 0; i < strlen(s); i++) {
        letter[s[i] - 'a']++;
    }
    for (int i = 0; i < strlen(t); i++) {
        letter[t[i] - 'a']--;
    }
    for (int i = 0; i < 26; i++) {
        if (letter[i] != 0) {
            return false;
        }
    }

    return true;
}

总结

  1. 要清楚哈希表使用的场景,一般哈希表都是用来快速判断一个元素是否出现集合里,可以查看这篇文章了解哈希表。

LeetCode 349. 两个数组的交集

题目链接:349. 两个数组的交集
文章讲解:代码随想录#349. 两个数组的交集
视频讲解:学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集

题目描述

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

示例1

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例2

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

提示

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000

思路

需要求出交集的元素值,由于两个数组的值<=1000,所以可以构造一个哈希数组arr[1000],哈希数组的下标为原始数组的元素值,哈希数组的值为原始数组出现的次数。
同时还需要构建一个数组,用来记录交集。数组大小为原始两个数组中的最小的长度。

参考代码

int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size,
                  int* returnSize) {
    int arr[1000] = {0}; // 构建一个哈希级数,大小为1000
    int sizeNum = 0;

    int size = nums1Size > nums2Size
                   ? nums2Size
                   : nums1Size; // 取两者最小值用来构建交集数组的大小
    int* ret = (int*)malloc(size * sizeof(int));

    for (int i = 0; i < nums1Size; i++) {
        arr[nums1[i]]++;
    }
    for (int i = 0; i < nums2Size; i++) {
        if (arr[nums2[i]] != 0) {
            ret[sizeNum++] = nums2[i];
            arr[nums2[i]] = 0; // 清除,防止后面出现相同的数组,重复计算
        }
    }
    *returnSize = sizeNum;
    return ret;
}

总结

  1. 对于哈希表时刻要明白哈希数组下标代表什么含义,其值又代表什么含义。
  2. 哈希数组的限定要求就是数量不能太大,以及值不能离散得太厉害了。

LeetCode 202. 快乐数

题目链接:202. 快乐数
文章讲解:代码随想录#202. 快乐数

题目描述

编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

示例1

输入:n = 19
输出:true
解释:
1^2+ 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1

示例2

输入:n = 2
输出:false

思路

这道题的题眼是可能存在无限循环,意味着在求和的过程中有些值会重复出现,那么我们就可以考虑使用哈希法了,这样就可以快速判断在集合中某个值是否重复出现。
但是这个集合大小是未知的,不能直接使用哈希数组,而C语言中不像C++中直接可以使用set,需要自己封装set集合,所以处理起来稍微麻烦一些。

参考代码

// 通过链表实现set
struct key {
    int val;
    struct key* next;
};

typedef struct key Set;

// 初始化Set
Set* initSets(void) {
    Set* head = (Set*)malloc(sizeof(Set));
    head->next = NULL;
    return head;
}

// 给Set中添加元素
void addSetsElement(Set* head, int val) {
    Set* cur = head;

    while (cur->next != NULL) {
        cur = cur->next;
    }

    Set* node = (Set*)malloc(sizeof(Set));
    node->next = NULL;
    node->val = val;
    cur->next = node;
}

// 从Set中判断是否存在val
int isSetsExisted(Set* head, int val) {
    Set* cur = head;

    while (cur != NULL) {
        if (cur->val == val) {
            return 1;
        }
        cur = cur->next;
    }
    return 0;
}

int getSum(int n) {
    int sum = 0;
    do { // 拆分数字每位,计算平方和
        sum += (n % 10) * (n % 10);
    } while (n /= 10);
    return sum;
}

bool isHappy(int n) {
    Set* head = initSets();
    int num = 0;

    while (1) {
        num = getSum(n);
        if (num == 1) { // 如果平方和为1,则为快乐数
            return true;
        }
        if (isSetsExisted(head, num)) { // 如果num已经存在于Set中,说明已经开始重复循环了
            return false;
        }
        // 给Set中添加num
        addSetsElement(head, num);
        n = num; // 更新n值,进行下一次计算
    }
}

总结

  1. 链表就这么用起来了,set集合的实现应该也可以通过一些头文件来实现,后面有机会再优化下。

LeetCode 1. 两数之和

题目链接:1. 两数之和
文章讲解:代码随想录#1. 两数之和
视频讲解:梦开始的地方,Leetcode:1.两数之和

题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例1

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例2

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例3

输入:nums = [3,3], target = 6
输出:[0,1]

提示

  • 2 <= nums.length <= 104
  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9
  • 只会存在一个有效答案

思路

这道题中元素中可能存在比较大的值,就不能再单纯使用哈希数组了,那么就需要用到map。
C语言中使用哈希map需要用到一些宏和头文件,可以参考这篇文章

假如有数组[2, 7, 11, 15],目标target为9。
首先构建一个map,key为数组元素,value为元素下标。
接着开始遍历数组,当i=0时,值为2,与目标9比较,还差7,
然后去map中查找是否存在7,如果存在7,则返回map中value即下标,以及当前索引0。
如果不存在7,则将数值2存入到key中,将索引0存入到value中。
接着遍历i=1,数值为7,差值为2,则去map中查找key中是否存在值为2的数,如果存在,则返回2的下标,以及当前下标。
如果不存在,则将数值7存入到key中,将索引1存入到value中,以此类推…

参考代码

// 通过链表实现map
struct key {
    int val;
    int idx;
    struct key* next;
};

typedef struct key map;

// 初始化map
map* initMap(void) {
    map* head = (map*)malloc(sizeof(map));
    head->next = NULL;
    return head;
}

// 向map中添加元素
void addMapElement(map* head, int val, int idx) {
    map* cur = head;

    while (cur->next != NULL) {
        cur = cur->next;
    }

    map* node = (map*)malloc(sizeof(map));
    node->next = NULL;
    node->val = val;
    node->idx = idx;
    cur->next = node;
}

// 判断map中是否存在val
int isMapExisted(map* head, int val) {
    map* cur = head;

    while (cur != NULL) {
        if (cur->val == val) {
            return cur->idx;
        }
        cur = cur->next;
    }
    return -1;
}

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    int diff = 0;
    int j = 0;
    int *ret = (int*)malloc(2*sizeof(int));
    *returnSize = 2;
    map* head = initMap();
    for (int i = 0; i < numsSize; i++) {
        diff = target - nums[i];
        j = isMapExisted(head, diff);
        if (j != -1) {
            ret[0] = i;
            ret[1] = j;
            return ret;
        }
        addMapElement(head, nums[i], i);
    }
    return NULL;
}

总结

  1. 这块是我的知识盲区,需要多看多练
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值