代码随想录训练营第五天|哈希表基础、 242.有效的字母异位词、349. 两个数组的交集 、 202. 快乐数 、 1. 两数之和

一、哈希表基础

文章讲解: 代码随想录

1.当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。牺牲了空间换取了时间

 2.官方解释:哈希表是根据关键码的值而直接进行访问的数据结构。

哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素

3.时间复杂度从枚举的O(n)到通过索引直接查询的O(1)

4.哈希函数:将存放的元素映射到索引上,就可以通过查询索引下标快速判断一个元素是否在集合里。hashcode通过特定编码方式,可以将其他数据格式转化为不同的数值。(取模)

具体例子看上文链接:

5.哈希碰撞:由于哈希函数取模或元素数量过多等情况导致映射到同一个下标时,哈希碰撞

拉链法:碰撞后,都存放在同一个下标处(链表)

线性探测法:碰撞后,向下寻找空位存放(哈希表要大)

周末看看是如何代码实现的

6.数组:缺点:哈希值未知,哈希值比较少、特别分散、跨度非常大时不建议使用数组(需要申请比较大的数组空间间并会出现浪费),哈希函数可能是自己写的

7.set(集合):数量未知,根据有序,无序,是否重复选择使用

                     set是一个集合,里面放的元素只能是一个key

  可以认为unordered_set 存储的都是键和值相等的键值对,为了节省存储空间,该类容器在实际存储时选择只存储每个键值对的值。即哈希映射的索引为值。 

   缺点:直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。

8.map(映射)map是一种key value的存储结构,可以用key保存数值,用value再保存数值所在的下标。

详细的区别:代码随想录

二、有效的字母异位词

题目链接/文章讲解/视频讲解: 代码随想录

1.哈希映射函数 :s[i]-'a' 将每一个元素映射到数组下标(索引)处 字母到数组索引可以用ascii值

本题可以通过减去一个相对值'a'来建立,一开始我还是用ascii值26来处理

2.查找元素:时间复杂度O(1),通过索引直接查找

3.还可以使用multiset完成

        for(int j=0;j<t_size;j++)
        {
            array[t[j]-'a']--;
        }

代码:数组

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

 三、两个数组的交集

题目链接/文章讲解/视频讲解: 代码随想录

1.比较简单,就是存入哈希表,再查找判断

2.时间复杂度: O(n + m) m

3.使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复

4.本题因为数据有范围了使用了数组

5.数组去重

for (int num : nums1) 
{ 
  hash[num] = 1;//数组去重操作
}

代码:set

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

代码:数组

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result; 
        int hash[1001] = {0}; 
        for (int num : nums1) { 
            hash[num] = 1;//数组去重操作
        }
        for (int num : nums2) { 
            if (hash[num] == 1) {
                result.insert(num);//结果去重
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

四、快乐数 

题目链接/文章讲解: 代码随想录

 1.如果单单是按数学逻辑写的话,会出现时间超出限制,因为会出现无限循环问题

无限循环终止条件:出现相同的sum值(查找是否有元素使用哈希表)

2.这题显然不考虑使用数组,使用set比较方便

代码

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

五、两数之和

题目链接/文章讲解/视频讲解: 代码随想录

1.本题需要查找元素是否在集合内(在该数组中找出 和为目标值 target  的那 两个 整数)

2.数组的缺点:.数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。

set的缺点:set是一个集合,里面放的元素只能是一个key。本题需要返回元素下标,所以不合适

所以本题以上两种方法都不合适,采用map

3.需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适。

4.这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。

那么判断元素是否出现,这个元素就要作为key,所以数组中的元素作为key,有key对应的就是value,value用来存下标。

map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。

5.代码的逻辑:先查询再插入,边查询边插入(查询这个元素的前是否有满足要求的另一个数),(本题答案是唯一 的)这样可以避免(如6=3+3)相同key值的情况,下标不一样的情况(一个在map中一个正在通过vector访问)

                        也可以先全部插入再查询,使用multimap,比较繁琐。

代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        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中的元素在i之前
            }
            // 插入
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};
    }
};

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值