Day6哈希表(一)【day5周天休息】

day6
2023.12.4
代码随想录
今天开始新的结构—哈希表;也就是散列表,哈希表的理论知识在考研是滚瓜烂熟,各种哈希函数、冲突解决方法、平均查找长度等等。不过还是想吐槽一下,当时专业课考试的哈希表题给我做懵了,至今记忆犹新,原题目:给了两个哈希函数,然后表中已经插入了两个值,让把剩下值插入。。。当时看了半天,不会,不知道怎么插的,两个哈希函数的作用也不知道。。。第一小问都不会。。更别提第二问求平均查找长度了。现在也不清楚那道题到底怎么做。。。ok,言归正传,对于哈希表的相关用法,好像从没写过相关代码,仅接触理论知识,希望借此机会能提升一下相关代码水准

1. 242有效的字母异位词
这道题开始有些束手无策,很呆瓜的办法,遍历然后看每个字母出现次数,对比即可。难不成26个case?emmm,显然不是正解,也是看了代码随想录的文字讲解才懂了哈希表的做法,一种映射机制。个人看来,本题方法有两个关键点
1.映射机制,26个英文字母,对应0-25,并且字母-'a’就是对应的数字,感觉很巧妙,学到了
2.开始以为遍历两个字符串后,对比两个哈希表数组是否相等即可,但空间开销大一点,正解是在第一个数组记录时++,在遍历第二个字符串时–,如果满足要求,最终数组应该都是0,很巧妙,学会了。

class Solution {
public:
    bool isAnagram(string s, string t) {
        int reword[26] = {0};
        for(int i=0;i<s.size();i++){
            reword[s[i]-'a']++;
        }
        for(int i =0;i<t.size();i++){
            reword[t[i]-'a']--;
        }
        for(int i=0;i<26;i++){
            if(reword[i]!=0)
                return false;
        }
        return true;
    }
};

2. 349两个数组的交集
这道题有了第一题的思想,还是挺简单了,不过空间开销能大一点,两个数组表示哈希表(题目给了数值范围),数组下标对应给的两个数组的值,也是一种映射,最后比较两个数组,如果同时都不为0,则将索引存入结果数组中,这是我得写法,很普通,时间开销也不大,但就是空间开销大了点。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        int res1[1000] = {0};
        int res2[1000] = {0};
        vector<int> result;
        for(int i=0;i<nums1.size();i++){
            res1[nums1[i]]++;
        }
        for(int i=0;i<nums2.size();i++){
            res2[nums2[i]]++;
        }
        for(int i = 0;i<1000;i++){
            if(res1[i]!=0 && res2[i]!=0)
                result.push_back(i);
        }
        return result;
    }
};

不过代码随想录中给了一种新的写法,很方便,相比之下之前代码就很冗余了。以下是代码随想录中的解释:
而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

此时就要使用另一种结构体了,set ,关于set,C++ 给提供了如下三种可用的数据结构:

  • std::set
  • std::multiset
  • std::unordered_set

std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

3. 202快乐数
这道题还是挺新颖的,刚看到还是有些懵,首先求和就有点困难,其次就是本题的关键点,怎么不是快乐数?也就是无限循环,但怎么会无限循环呢,肯定是有个数重复出现了,因此一直重复该过程,导致无限循环,所以大体思路就是,将每次sum存放在一个结构中,然后判断和是否为1,是则true,不是则继续循环,并且判断新sum与之前所有sum是否存在重复,一旦有重复,则false,不是快乐数
判断一个数是否在一个集合中,哈希表最快捷!因此本次最优解是使用哈希表。

class Solution {
public:
    int getNum(int n){
        int num=0;
        while(n){
            num += (n%10)*(n%10);
            n = n/10;
        }
        return num;
    }
    bool isHappy(int n) {
        unordered_set<int> result;
        while(1){
            int num = getNum(n);
            if(num==1)
                return true;
            if(result.find(num)!=result.end()){
                return false;
            }
            else{
                result.insert(num);
            }
            n = num;
        }
    }
};

4. 1两数之和
这道题看似简单,实则还是有难度的感觉,简单的暴力遍历就不说了,肯定不是我们想要的正解,我们想要一个循环解决,每次循环体代表一个数,想找到另一个数以匹配target,是不是也是在一个集合中找到某个值?这不就是哈希表的用处么,不过不同的是,暴力for循环是在内层for中遍历寻找的,而我们要的是不用for,我们定义一个map,key表示值,value表示索引,遍历中如果当前值不满足要求,就放入map中,然后每次for循环在map中找需要的另一个匹配的值。有点在之前值里找答案的感觉,而两层for是在之后未知数据中找值,这是我个人的理解。如果遍历完后,找不到值,返回空,找到了,将对应循环游标i和map的key返回即可

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map<int, int> temp;
        for(int i=0;i<nums.size();i++){
            auto n = temp.find(target-nums[i]);  //在以往存储的map中找另一个值
            if(n!=temp.end()){  //这是找不到的情况,也就是遍历完后最终返回最后一个值
                return {n->second, i};
            }
            temp.insert(pair<int,int>(nums[i],i));
        }
        return {};
    }
};
  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值