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

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

一、哈希表理论基础

哈希表

  • 一般哈希表都是用来快速判断一个元素是否出现集合里

哈希函数

​ 哈希函数,把值直接映射为哈希表上的索引,然后就可以通过查询索引下标快速找到对应的值。

哈希碰撞

​ 如图所示,小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞

在这里插入图片描述

拉链法

​ 刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了

在这里插入图片描述

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

线性探测法

例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示:

在这里插入图片描述

常见的三种哈希结构

当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。

  • 数组
  • set (集合)
  • map(映射)

这里数组就没啥可说的了,我们来看一下set。

在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:

在这里插入图片描述

std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。

在这里插入图片描述

​ std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。

当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

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

其他语言例如:java里的HashMap ,TreeMap 都是一样的原理。可以灵活贯通。

虽然std::set、std::multiset 的底层实现是红黑树,不是哈希表,std::set、std::multiset 使用红黑树来索引和存储,不过给我们的使用方式,还是哈希法的使用方式,即key和value。所以使用这些数据结构来解决映射问题的方法,我们依然称之为哈希法。 map也是一样的道理。

这里在说一下,一些C++的经典书籍上 例如STL源码剖析,说到了hash_set hash_map,这个与unordered_set,unordered_map又有什么关系呢?

实际上功能都是一样一样的, 但是unordered_set在C++11的时候被引入标准库了,而hash_set并没有,所以建议还是使用unordered_set比较好,这就好比一个是官方认证的,hash_set,hash_map 是C++11标准之前民间高手自发造的轮子。

二、242有效的字母异位词

​ 数组就是简单的哈希表,但是数组的大小可不是无限开辟的,这题对应的值不多,可以开辟大小为26的数组来存储字符出现的次数,然后索引则用字符a到字符z的ASCII表的相对位置表示。

class Solution {
public:
    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. 两个数组的交集

​ 这道题主要考察set数据类型的哈希表应用,C++语法还没学完做这道题有点吃力,只能边学边用了,看了carl哥的代码才写的出来,明天需要再刷一遍。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result;
        unordered_set<int> num_set(nums1.begin(), nums1.end());
        for(int i = 0; i < nums2.size(); i++){
            if(num_set.find(nums2[i]) != num_set.end())
                result.insert(nums2[i]);
        }
        return vector<int>(result.begin(),result.end());
    }
};

四、202.快乐数

​ 这道题仍然是采用set数据类型的一道题,本题一刷通过,一刷思路如下:循环每次判断新得到的平方和是不是set里已经有的数,如果没有就把改数组加入set里,一直循环直到出现平方和为1或者新得到的平方和已经在set里了。代码如下:

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> num_set;
        int sum = n;
        while(num_set.find(sum) == num_set.end()){
            num_set.insert(sum);
            int length = get_length(sum);
            int temp = 0;
            while(length--){
                temp += (sum % 10) * (sum % 10);
                sum = sum/10;
            }
            sum = temp;
            if(sum == 1)    return true;
        }
        return false;
    }
    int get_length(int x){
        int length = 1;
        while(x /10 != 0){
            x = x/10;
            length++;
        }
        return length;
    }
};

​ 解题思路大体上是对的,但是求和的操作有点笨拙所花的时间更多,修改后的代码:

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> num_set;
        int sum = n;
        while(num_set.find(sum) == num_set.end()){
            num_set.insert(sum);
            sum = get_num(sum);
            if(sum == 1)    return true;
        }
        return false;
    }
    int get_num(int x){
        int sum = 0;
        while(x){
            sum += (x % 10) * (x % 10);
            x /= 10;
        }
        return sum;
    }
};

五、1.两数之和

​ 这题思路暴力解法比较简单,写两个for循环遍历一下就行了,复杂度为O(n^2)。由于题目要求返回对应的数组下标,因此考虑采用map的数组形式,但对map数据结构不太熟悉,直接看的解析。

​ 通过map存储已经遍历过的值,然后判断map里是否存在target于当前遍历的值的差值,如果存在返回map对应key的value,不存在将当前遍历的值存入map。代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map<int,int> number;
        for(int i = 0; i < nums.size(); i++){
            auto iter = number.find(target - nums[i]);
            if(iter != number.end()){
                return {iter->second,i};
            }
            number.insert(pair<int,int>(nums[i],i));
        }
        return {};
    }
};

六、总结

​ 今天学习的内容是新的一个数据结构,题目都不是很难,但是对哈希表的操作上不够了解,明天二刷一遍加深印象和了解。抓紧时间把C++语法书跟上!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值