Day 5 哈希表1

哈希表的基本概念

1. 定义:一般哈希表都是用来快速判断一个元素是否出现集合里,但是哈希法也是牺牲了空间换取了时间。将元素映射到哈希表上就涉及到了hash function ,也就是哈希函数。

2. 解决哈希碰撞的方式是拉链法(用链表)和线性探测法。

3. 实现方式:

        红黑树是key有序的平衡二叉树。优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

        map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制的,因为key的存储方式使用红黑树实现的。

242.有效的字母异位词

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

思路:

这道题属于查找,可以想到用哈希表进行。这道题可以利用数组作为哈希表。

解答:

将26个英文字母的ASCII码作为key,查找ASCII码的方法是用字符与'a'作差。

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

349. 两个数组的交集

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

思路:

这道题同样需要使用哈希表,但是数组不太行,因为不知道最大的元素是什么。即使通过遍历得知了最大元素,哈希表也会比较稀疏。因此想到使用链表,但是链表因为索引的时间复杂度为O(n)并不能充当哈希表。

解答:

1. 容器的选择:

使用数组来做哈希的题目,是因为题目都限制了数值的大小。如果哈希值比较少、特别分散、跨度非常大,就要使用另一种结构体了,set。关于set,C++ 给提供了如下三种可用的数据结构:

  • std::set:红黑树,key有顺序
  • std::multiset:红黑树,key有顺序,元素可以重复
  • std::unordered_set:哈希表,key无顺序,读写效率最高

根据这道题的需求,我们不需要让key有顺序,因此使用unordered_set容器。

2. 容器的使用:

(1)容器的复制

nums1.begin()nums1.end() 是指向容器 nums1 的迭代器。nums1 是一个 vector<int> 类型的对象。这两个函数通常用来获取容器的起始和结束位置的迭代器nums1.begin() 返回指向第一个元素的迭代器,如果 vector 为空,则 nums1.begin() 的值等同于 nums1.end()nums1.end()是一个指向 vector最后一个元素之后位置的迭代器,它并不指向任何有效的元素,而是用于标记容器的结束。当这两个迭代器一起使用时,它们通常表示容器的整个范围。例如,在代码 unordered_set<int> nums_set(nums1.begin(), nums1.end());

(2)容器的元素查找

在 C++ 中,nums_set.find(num) 尝试在 nums_set 中找到一个值为 num 的元素。如果找到了这个元素,find 方法返回一个指向该元素的迭代器。如果没有找到,它返回一个指向 unordered_set 结束位置的迭代器,即 nums_set.end()

(3)容器的遍历

for (int num : nums2) 是一个范围基于的 for 循环(也称为基于范围的 for 循环或范围 for 循环),简洁地遍历容器(如数组、向量、集合等)中的所有元素。

代码:

vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set;
        unordered_set<int> nums_set(nums1.begin(),nums1.end());
        for(int num:nums2){
            if(nums_set.find(num)!=nums_set.end()){
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(),result_set.end());
    }

 

202. 快乐数

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目解答:代码随想录

思路:

这道题的关键在于判断什么情况下会陷入无限循环。这里无限循环可以用哈希表来实现。

感悟:

识别无限循环的方法至今有两种,一种在链表中,用快慢指针识别。这里快慢指针还可以用来判断循环开始的位置。这里对循环节没有限制

另一种是哈希表。但这种方式需要循环节内部没有重复元素。换句话说,循环起始位置就可以识别循环的开始。对于这道题,哈希表是更好的方式。

代码:

bool isHappy(int n) {
        unordered_set <int> exist_set;
        int cal_num = n;
        do{
            int cal = 0;
            do{
                cal+=(cal_num%10)*(cal_num%10);
                cal_num/=10;
            }while(cal_num);
            if(exist_set.find(cal)==exist_set.end()){
                exist_set.insert(cal);
                cal_num = cal;
            }else{
                return false;
            }
        }while(cal_num!=1);
        return true;
    }

1. 两数之和

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

思路:

这道题的思路是哈希表,但是需要dictionary的数据类型。每次从字典中找对称元素。

解答:

(1)什么时候使用哈希法:当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。 

(2)字典数据类型map:

(3)字典的插入:

map.insert(pair<int, int>(nums[i], i)) 这行代码用于向 map 中插入一个新的键值对。pair<int, int>(nums[i], i): 这创建了一个 std::pair 对象,它是一个将两个值组合成一个单一对象的简单容器。

代码:

vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            auto iter = map.find(target - nums[i]);
            if(iter != map.end()){
                return {iter->second, i};
            }
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};
    }
  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值