LeetCode刷题笔记【5】:哈希表专题-1(有效的字母异位词, 两个数组的交集, 快乐数, 两数之和)

前置知识

什么是哈希表(Hash Table)?

哈希表(散列表)是根据关键码的值而直接进行访问的数据结构。

哈希函数

相比于数组, 哈希表通过哈希函数, 将一个元素根据其内容映射到哈希表中, 从而实现元素的快速查找.
在这里插入图片描述
哈希函数通过编码的方式进行映射, 上述例子中是通过多次取模的操作, 将地址取到tableSize的范围内.

但是如果有多个学生姓名取到了同一位置怎么办?
这就涉及到哈希碰撞的解决.

哈希碰撞

在这里插入图片描述

用拉链法解决哈希碰撞

在这里插入图片描述
将发生冲突的元素储存于链表

用线性探测法解决哈希碰撞

在这里插入图片描述
存放和查找小王的时候, 如果发现小李了, 就依次向下一个位置查找/存放.
此时要求tableSize>dataSize, 不然没地方存放冲突数据.

哈希类型的数据结构都有哪些?

在这里插入图片描述
在这里插入图片描述
想要使用哈希法解决问题的时候, 一般选择使用数组, 集合(set), 映射(map)

在以上的这些set和map中, 一般使用unordered_setunordered_map, 因为他们的**查询与增删效率都是O(1)**的

什么时候使用哈希表?

一般哈希表都是用来快速判断一个元素是否出现集合里.
所以, 在需要频繁判断一个元素是否在集合中时, 就考虑哈希法
(或者判断一个元素是否出现过)

同时因为哈希法本质是牺牲空间换时间, 所以更适合对于空间要求不高, 但是对时间要求高的场景.

> 参考文章:https://programmercarl.com/%E5%93%88%E5%B8%8C%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

242. 有效的字母异位词

题目描述

截图

LeetCode链接:https://leetcode.cn/problems/valid-anagram/

unordered_map记录字母出现频率

思路: 用unordered_map<char, int>记录s和t中每个字母出现的频率. 挨个比较

class Solution {
public:
    bool isAnagram(string s, string t) {
        unordered_map<char, int> sSet, tSet;
        for(char c : s){
            sSet[c]++;
        }
        for(char c : t){
            tSet[c]++;
        }
        for(char c : s){
            if(sSet[c]==tSet[c])
                continue;
            else
                return false;
        }
        for(char c : t){
            if(sSet[c]==tSet[c])
                continue;
            else
                return false;
        }
        return true;
    }
};

用字母数组记录字母出现频率

可以使用一个26位的数组实现记录字母出现频率的功能.

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.length() != t.length())
            return false;
        vector<int> counter(26, 0);
        for(char c : s){
            counter[c-'a']++;
        }
        for(char c : t){
            counter[c-'a']--;//这里其实很奇妙
            if(counter[c-'a']<0)
                return false;
        }
        return true;
    }
};

代码中说那里很奇妙, 原因是:
这里似乎只考虑了t中某字母出现次数比s中多的情况, 没有考虑t中同一字母少的情况.
但其实是没有问题的
.
因为刚开始时判断过两个字符串长度相同, 那么当两个字符串中字母频率不同时, 就一定是某个字母多了, 某个字母少了, 那么就一定会被检测出来.
真妙啊.

349. 两个数组的交集

题目描述

截图

LeetCode链接:https://leetcode.cn/problems/intersection-of-two-arrays/

解题思路

思路: 使用unordered_set, 在insert的过程中自动就去重了.
具体用set1记录nums1中的元素;
遍历nums2, 发现重复元素就加入set2;
最后将set2转化为vector<int>输出

代码

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1, set2;
        for(int num : nums1){
            set1.insert(num);
        }
        for(int num : nums2){
            if(set1.count(num))
                set2.insert(num);
        }
        // vector<int> ans;
        // for(auto it : set2)
        //     ans.push_back(it);
        // return ans;
        return vector<int>(set2.begin(), set2.end());
    }
};

202. 快乐数

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/happy-number/description/

解题思路

题目本身不难, 但是其数学原理难以想到

数学原理讲解

代码

class Solution {
public:
    int multiple(int n){
        int ans=0;
        while(n){
            ans += (n%10) * (n%10);
            n /= 10;
        }
        return ans;
    }
    bool isHappy(int n) {
        unordered_set<int> set;
        while(!set.count(n)){
            // cout << "n= " << n << endl;
            if(n==1)
                return true;
            set.insert(n);
            n = multiple(n);
        }
        return false;
    }
};

Num. Name 4

题目描述

截图

LeetCode链接:xxx(记得加点击跳转链接)

暴力求解

两层循环, O(n^2)求解

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n=nums.size();
        for(int i=0; i<n; ++i){
            for(int j=i+1; j<n; ++j){
                if(nums[i]+nums[j] == target){
                    return {i, j};
                }
            }
        }
        return {};
    }
};

过程中建立unordered_map

很容易想到的一种解法是:
先建立unordered_map<值, 下标>, 然后依次遍历, 在遍历到值为val的时候, 查找值为target-val的元素.

但是这样有个问题, 那就是当val=target/2时, 会查到自己, 然后就出问题了.

所以不能从一开始就建立哈希表, 要在遍历的过程中逐步建立, 即:
遍历过程中, 对于当前元素, 如果能在哈希表中找到target-val的对应元素, 就返回{i, map[target-val]};
没找到的话, 就将当前元素加入map.

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n=nums.size();
        unordered_map<int,int> map;
        for(int i=0; i<n; ++i){
            if(map.count(target-nums[i])){
                return {i, map[target-nums[i]]};
            }else{
                map[nums[i]] = i;
            }
        }
        return {};
    }
};

总结

对于哈希表的问题, 要牢记: 大量的检测元素是否存在时, 使用哈希表.

并且在使用unordered_map<key, val>时, 需要考虑用什么当key, 以及val到底存储什么.

关于set.find()!=set.end() 和 set.count()

二者都可以用于判断哈希表中有无某元素.

find() 返回一个有效的迭代器,我们可以使用它来访问该元素。如果未找到元素,则 find() 返回 mySet.end(),并且我们可以根据这一结果输出相应的消息。

相比之下,count() 只返回元素出现的次数,并不提供具体的位置或迭代器。如果你只是想知道元素是否存在并不需要具体的位置信息,那么使用 count() 是可以的。但如果你希望获取元素的位置或进行后续的操作,使用 find()end() 更加灵活。

总结起来,使用 set.find() != set.end() 而不是 set.count() 的原因是为了获取更多的操作灵活性,包括访问元素以及处理元素不存在的情况。

如果只是想要检测一下有没有这个元素存在, 那么count()足够了.

本文参考:
242.有效的字母异位词
349. 两个数组的交集
202. 快乐数
1. 两数之和

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值