【代码随想录】哈希

242. 有效的字母异位词

原题链接

算法思想:哈希,排序

哈希思想:重点就是迅速查找,通过key和值的迅速匹配,所以当题目是与 “唯一性” 有关时可用
用哈希表维护字母出现的次数

方法一:哈希

我的做法:用了map

class Solution {
public:
    bool isAnagram(string s, string t) {
         if(s.size()!=t.size())  //如果没有这句会"a","a,b"错误
              return false;
        map<char,int> a;
        for(int i=0;i<s.size();i++){
            a[s[i]]++;
        }
        for(int i=0;i<t.size();i++){
            if(a.count(t[i])==1){
                a[t[i]]--;
            }
        }
        for(int i=0;i<a.size();i++)
        {
            if(a[i]!=0)
                return false;
        }
        return true;

    }
};

官方
使用的是数组来表示哈希,一个26个元素的数组,将每个字母-‘a’,对应到数组的位置下标。

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

    }
};

方法二:排序

题目等价于排序后看两个字符串是否相等.
sort对string排序: sort( str.begin(), str.end() );

class Solution {
public:
    bool isAnagram(string s, string t) {
       sort(s.begin(),s.end());
       sort(t.begin(),t.end());
       if(s==t)
           return true;
        return false;
    }
};

383. 赎金信

原题链接

这里用数组比用undered_map简单多了
数组直接天然顺序0-25 就是a-z,直接一对一比较,map还要找key再比较
结论:string类型查找时,只有小写字母,使用数组当哈希很方便!

注意:
该观点错误:【map不能重复插入,计算次数,每次还要判断是不是第一次插入】map遇到key值相同的字符时,可以直接mp[key]++, 计算次数。,而insert不会进行覆盖,每次需要判断是否已经存在,如果存在,则[key]++,不存在插入{key,0},见下方的引出问题:

引出问题:map出现相同key值时,会怎么处理?

当使用insert进行相同key值插入时,不会进行覆盖,原理:insert源码是先进行遍历了,如果出现相同key值,就直接返回,如果没有则插入;
当使用[ ],是重载的,所以可以进行相同key值的覆盖。

哈希数组:

 vector<int>r(26);
        vector<int>m(26);
        for(int i=0;i<magazine.size();i++){
               m[magazine[i]-'a']++;
        }
        for(int j=0;j<ransomNote.size();j++){
            r[ransomNote[j]-'a']++;
        }
        for(int i=0;i<26;i++){
            if(m[i]<r[i])
            {
                return false;
            }

        }
        return true;

哈希map:

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
         //方法一:先排序后遍历
         //方法二:magazine存储到哈希,遍历ransomNote,再在magazine中是否全可以查找得到
        //  if(magazine.size()<ransomNote.size())
        //     return false;
        //  sort(ransomNote.begin(),ransomNote.end());
        //  sort(magazine.begin(),magazine.end());
        //  for(int i=0;i<magazine.size();i++){
        //      if(magazine[i]==ransomNote[i])
        //           return false;
        //  }
        //  return true;

        unordered_map<char,int>mp;
        unordered_map<char,int>mp1;
        for(int i=0;i<magazine.size();i++)
        {
            //key唯一
                mp[magazine[i]]++;
           
        }
        for(int i=0;i<ransomNote.size();i++){
            
                mp1[ransomNote[i]]++;
         
        }
        for(auto it: mp1){
            if(mp.count(it.first)==0)
              return false;
            if(it.second>mp[it.first]){
                return false;
            }
        }
        return true;
       
        //不是m中的个数只要>=0即可,不是不等于0,因为m包含r,m中的次数可能比r多,只要m次数>=r可以
        

    }
};

349. 两个数组的交集

原题目链接
算法思路:排序+双指针
我的做法:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
        int len1=nums1.size();
        int len2=nums2.size();
        vector<int> v;
        set<int> s;
        set<int>:: iterator it;
  //      int minlen=len1<len2?len1:len2;
        int p=0,q=0;
        while(p<len1&&q<len2){
               if(nums1[p]==nums2[q]){
                  //  v.push_back(nums1[p++]);
                  s.insert(nums1[p++]);
                    q++;
               }else if(nums1[p]<nums2[q])
               {
                   p++;
               }
               else{
                   q++;
               }      
        }
        for(it=s.begin();it!=s.end();it++){
            v.push_back(*it);
        }
        return v;
    }
};

冗余点:
唯一性判断可以通过有序这个特性,不用再设置set,因为每次加入到vector里的元素也是有序的。

官方:
因为是有序的,通过比较插入vector元素的值和v.back()是否相等来确定插入元素的唯一性。注意当size()==0时,即没有元素,调用v.back()会报错!

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        sort(nums1.begin(), nums1.end());
        sort(nums2.begin(), nums2.end());
        int length1 = nums1.size(), length2 = nums2.size();
        int index1 = 0, index2 = 0;
        vector<int> intersection;
        while (index1 < length1 && index2 < length2) {
            int num1 = nums1[index1], num2 = nums2[index2];
            if (num1 == num2) {
                // 保证加入元素的唯一性
                if (!intersection.size() || num1 != intersection.back()) {
                    intersection.push_back(num1);
                }
                index1++;
                index2++;
            } else if (num1 < num2) {
                index1++;
            } else {
                index2++;
            }
        }
        return intersection;
    }
};

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/intersection-of-two-arrays/solution/liang-ge-shu-zu-de-jiao-ji-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

时间复杂度:O(mlogm+nlogn)
其中 m 和 n分别是两个数组的长度。对两个数组排序的时间复杂度分别是 O(m log m)和 O(nlog n),双指针寻找交集元素的时间复杂度是 O(m+n),因此总时间复杂度是O(mlogm+nlogn)。
空间复杂度:O(log m+log n)
其中 m 和 n 分别是两个数组的长度。空间复杂度主要取决于排序使用的额外空间。

随想录:
使用unordered_set

class Solution {
public:
    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) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

注意:
但是要注意,使用数组来做哈希的题目,是因为题目都限制了数值的大小。而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。
而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

202. 快乐数

原题链接
算法思路: 重点是找到循环出口!!看清楚快乐数和不是快乐数的返回条件是什么!
快乐数:最后结果为1
不快乐数:无限循环且值不可能为1
所以查找是否重复出现使用哈希,undered_set

class Solution {
public:
   bool isHappy(int n) {
        unordered_set<int> s;
        int sum=0;
        while(1){
           sum+=pow(n%10,2);
           n=n/10;
           if(n==0) //如果没有n==0,sum还没计算完,即计算到中间某一步,就开始查找了
            { 
                if(sum==1)
                   return true;
               if(s.count(sum)==0)                  {
                s.insert(sum);    
                n=sum;
                sum=0;   
                } 
               else 
                     return false;
            }
        }
 
    }
    
};

bug点

  1. 求一个数各个位的值,语句顺序影响结果!

1. 两数之和

原题链接
思路:暴力,哈希
哈希:
哈希可以用于查找是否出现,通过等式a+b=target来查找,此时,b=target-a;
使用unordered_set

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int,int> mp;
        // for(int i=0;i<nums.size();i++)
        // {
        //     mp[nums[i]]=i;
        // }
        for(int i=0;i<nums.size();i++){
            auto it=mp.find(target-nums[i]);
            if(it!=mp.end())
            {
                return {it->second,i};
            }
             mp[nums[i]]=i;
        }
        return {};
    }
};

bug点:

  1. 之前的思路先将数组存储到undered_set里,再遍历,在哈希表中查找,看是否出现了target-nums[i],这个时候,有两个错误点,一是map第一次遍历存储时,只能插入不相同的元素,如[3,3], map只能插入一个3;二是插入进去后,下一次找target-a时,如果是6-3=3这种,就会返回自己的坐标,如【3,2,4】 6
  2. 记住函数最后一定要返回值,否则报错
    随想录
    用的unordered_map

其他试错:
不能用双指针,首先,要返回的是原来的坐标,如果先排序再用两数之和,那么返回的left和right就是排序后的坐标。
如果不排序来求那么不能保证可以遍历到所有情况。
错误的双指针如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int left=0,n=nums.size();
        int right=n-1;
        sort(nums.begin(),nums.end());
        while(left<=right){
            int sum=nums[left]+nums[right];
            if(sum==target)
            {
                return {left,right};
            }else if(sum>target)
               right--;
            else left++;
        }
    return {0,0};  //记住必须要有返回值
    }
};

454. 四数相加 II

原题链接

算法思路:
由等式a+b=-(c+d),因为题目只要返回个数,所以就不用记录下标。将a+b的和存储起来,再遍历第三,四个数组看三+四的相反数是否存在和的数组中。
本来想着使用unordered_set, 但是可能和会重复,所以使用unordered_map,key为和,值为出现该和的次数。
遍历后两个数组时,就将sum加起来。

我的代码:

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>mp;
        int sum=0;
        for(int i=0;i<nums1.size();i++){
            for(int j=0;j<nums2.size();j++)
            {   
                // mp.insert(pair<int,int>(nums1[i]+nums2[j]));
                 mp[nums1[i]+nums2[j]]++;
            }
        }
        for(int i=0;i<nums1.size();i++)
           for(int j=0;j<nums1.size();j++){
               if(mp.count(-(nums3[i]+nums4[j])))
                    sum+=mp[-(nums3[i]+nums4[j])];
           }
        return sum;

    }
};

15 . 三数之和

原题链接

本题目用哈希麻烦

方法一:排序+双指针
二三重循环可以并行!所以出现了双指针
当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2)减少至 O(N),如本题和为固定值0,a,和值不变,b增大,c减小

本题目思路:
使用排序+双指针的大框架,加一些剪枝操作,如第一个数字如果是大于0 的,那么后面不可能三数之和为0,直接return 。
难点是去重操作,要答案不同,所以每当遍历到一个新的数字,都必须与前一个比较,看是否相同,三个数字都要去重。最容易出现bug的是出现死循环,当等于题目条件时,必须left,right都移动。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //与两数之和不同的是,两数之和的和是固定的target,本题目的c是不固定的,在数组里内
        //由此,可以想到,遍历该数组,每次都固定和不变,剩下的两个用双指针,找到后加入vector,这一点与两数之和的不同,因为不用输出下标,所以可以用双指针
       int n=nums.size();
       unordered_set<int> s;
       vector<vector<int>>v;
       int sum=0;
       sort(nums.begin(),nums.end());
        for(int i=0;
上一篇:Easyexcel(5-自定义列宽)-注解


下一篇:RabbitMQ 集群-前言