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

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

准备工作——哈希表的前前后后

  1. 什么是哈希表:
  • 通过关键码就可以立刻找到想要的元素,这就是哈希表(如数组)
  • 所谓关键码,也就是哈希函数(往往需要根据元素的特征来指定)
  1. 怎么用哈希表:

想要立刻在一群元素中快速、立即找到指定元素,就用哈希表(时间复杂度为O(1))

  1. 什么是哈希碰撞:
  • 一个关键码却对应两个或多个元素,即不能通过一个关键码唯一确定某个元素,这就叫哈希碰撞。
  • 处理方法:
    • 发生之前尽量改进关键码的规则
    • 拉链法(发生之后)——注意:表长(即tablesize)不要太长
    • 线性探测法(空置的位置来存放冲突的数据)(发生之后)——注意:必须满足tablesize>datasize

4.主要的三类:

  • 数组
  • set
    • unordered_set(优先使用)
    • set(关键码有序时再用)
    • multiset(关键码有序且有重复数据时再用)
  • map(与set同理)

242.有效的字母异位词

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

刚学了哈希表,我就想着怎么用set、map来解答,结果卡哥用的是数组。看来能用数组还是用数组更加方便。具体代码如下:

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

总体思路:

首先定义一个数组,来存储字符串里面每个字母使用的次数

然后问题来了:数组元素是次数,但是数组下标都是0123,又不是abcd,怎么知道这是哪个字母的次数?

答:将 s[i] 的值减去 ‘a’ ,那么 s[i]-‘a’ 的结果就是0123,于是实现了abcd向0123的转化。而且这是一一对应的,a对应0,b对应1,c对应2…

                hash[s[i]-'a']++

然后是最关键的逻辑将s中的字母存入之后,再删去hash[26]里面t存在过的所有字母及个数。如果最后hash[26]里的值全部为0,则s与t是字母异位词;反之则不是。

349. 两个数组的交集

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

解出这道题就需要理解unordered_set的性质和一些STL的基本操作,具体代码如下:

vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
    unordered_set<int> nums1_set(nums1.begin(),nums1.end());
        unordered_set<int> same;
        for(int i=0;i<nums2.size();i++)
        {
            if(nums1_set.find(nums2[i])!=nums1_set.end())
                same.insert(nums2[i]);
        }
        return vector<int>(same.begin(),same.end());
    }

总体思路比较简单:首先将nums1存放在一个set哈希表中(set作用:去重),然后将set与nums2对比,将相同的值放入另一个哈希表same中(作用同样是去重),最后将哈希表转换成vector的形式返回就行了。

一些STL 的补充:

  1. 定义:unordered_set set;

  2. 插入:

    • 范围插入(在初始化的时候也可以直接写)

      myrecipe.insert (mypantry.begin(),mypantry.end());`

    • 移动插入(即直接增加一个,实现动态加减)

      myrecipe.insert (make_pair<string,double>("eggs",6.0));

  3. 查找:

        c++ nums1_set.find(nums2[i]) 
    

如果找不到,就返回nums1_set.end();如果找到了,就返回指向该键值对的迭代器(指针)

202. 快乐数

题目链接:https://leetcode.cn/problems/happy-number/

1.暴力法(失败)

如果n是快乐数,能够判断出来;如果n不是快乐数,就会引发无限循环,代码就运行不了了…

bool isHappy(int n) {
        unordered<int> set;
        int sum=0;
            while(n!=0)
            {
                ge=n%10;
                sum+=ge*ge;
                n/=10;
                if(n==0)
                {
                    n=sum;
                    sum=0;
                }
                if(n==1)
                return true;
            }
    }
2.哈希法(unordered_set)
class Solution {
public:
    // 取数值各个位上的单数之和
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1) {
            int sum = getSum(n);
            if (sum == 1) {
                return true;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

这道题的核心逻辑:

1.如果sum的值重复出现两次,那么就一定会陷入无限循环,那一定不是快乐数;如果sum的值没有重复出现,并不能证明最后的结果就一定是1,还需要用if(sum==1)来判断一下。

2.为什么要用哈希表?就像卡哥所说的,当你的思路到了“要判断一个数是否存在于一个集合的时候”,就要用哈希表了。

另外:这道题用到了C++d一个重要思想:封装。把求位的平方和单独打包成一个函数,下面调用即可。可以最大程度避免逻辑混乱。

1.两数之和

题目链接:https://programmercarl.com/0001.%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C.html#%E6%80%9D%E8%B7%AF

这题我是二刷了,但是还是没有写对…,主要不是逻辑的问题,而是一些STL的基础问题。具体代码如下:

 vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            // 遍历当前元素,并在map中寻找是否有匹配的key
            auto iter = map.find(target - nums[i]); 
            if(iter != map.end()) {
                return {iter->second, i};
            }
            // 如果没找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};

map与set的不同之处在于,map是<int,int>, 是以键值对即 {键,值} 的形式保存的不仅保存元素,还保存元素的下标;而set是,只是保存一个元素,不保存元素对应的下标。

易错点:

1.谁是键 (key) , 谁是值 (value) ?

答:map.find( ) 内括号内的东西就是键

2.插入时也要按键值对的形式插入,即pair<int, int>(nums[i], i)

注:若 iter 为迭代器,则 key 即为 iter->first , value 即为 iter->second

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值