代码随想录算法训练营第六-七天|哈希表

三 哈希表

  • 快速判断一个元素是否出现集合里的时候,就要考虑哈希法

  • 数组,set ,map 都可以作为其数据结构

242. 有效的字母异位词

  • 给定两个字符串 *s**t* ,编写一个函数来判断 *t* 是否是 *s* 的字母异位词。

    注意:*s**t* 中每个字符出现的次数都相同,则称 *s**t* 互为字母异位词。

  • 示例 1:

     输入: s = "anagram", t = "nagaram"
     输出: true

    示例 2:

     输入: s = "rat", t = "car"
     输出: false
  • ​​​​​​​bool isAnagram(string s, string t) {
           int record[26] = {0};
     ​
             for(auto i:s)
             {
                 record[i-'a']++;
             }
             for(auto i:t)
             {
                 record[i-'a']--;
             }
             for(auto i:record)
             {
                 if(i != 0)
                 {
                     return false;
                 }
             }
     ​
             return true;
         }
  • 直接开数组做哈希表,统计出现的次数

349. 两个数组的交集

  • 给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

  • 示例 1:

    输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2] 示例 2:

    输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的

  • vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
             vector<int>record(1001,-1);   //用-1表示没出现过,跟0区分开
     ​
             vector<int>result;   
     ​
             for(auto &i: nums1)
             {
                 
                 record[i] = 1;
             }
             
             for(auto& i: nums2)
             {
                 if(record[i] == 1)
                 {
                     record[i] = 0;
                 }
             }
     ​
             for(int i =0;i < 1001;i++ )  //要处理的东西是recode 的下标
             {
                  if( record[i] == 0 )
                      result.push_back(i);
             }
             return result;
         }
  • 题目限制了数据的个数以及大小,所以建一个数组做哈希表

  • set的使用

  • vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
             unordered_set<int>result_set;
             unordered_set<int>nums_s(nums1.begin(),nums1.end());
     ​
             for(int i : nums2)
             {
                 if(nums_s.find(i) != nums_s.end())
                 {
                     //result_set.insert(i);
                     result_set.emplace(i);
                 }
             }
     ​
             return vector<int>(result_set.begin(),result_set.end());
         }

202. 快乐数

  • 编写一个算法来判断一个数 n 是不是快乐数。

    「快乐数」 定义为:

    • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。

    • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。

    • 如果这个过程 结果为 1,那么这个数就是快乐数。

    如果 n快乐数 就返回 true ;不是,则返回 false

  • 示例 1:

     输入:n = 19
     输出:true
     解释:
     12 + 92 = 82
     82 + 22 = 68
     62 + 82 = 100
     12 + 02 + 02 = 1

    示例 2:

     输入:n = 2
     输出:false
  • bool isHappy(int n) {
            unordered_set<int>record;
            while(record.find(n) == record.end()) // 没有找到
            { 
                record.emplace(n);
                int sum  = 0;
                while( n != 0 )
                {
                    sum += ( (n%10)* (n%10));
                    n = n/10;
                }
                if(sum == 1) return true;
                 n =sum;
            }
            return false;
        }
  • 和如果出现过就循环

1. 两数之和

  • 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

    你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

    你可以按任意顺序返回答案。

  • 示例 1:

     输入:nums = [2,7,11,15], target = 9
     输出:[0,1]
     解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

    示例 2:

     输入:nums = [3,2,4], target = 6
     输出:[1,2]

    示例 3:

     输入:nums = [3,3], target = 6
     输出:[0,1]
  • vector<int> twoSum(vector<int>& nums, int target) {
     // 暴力
             // int sum = 0;
     ​
             // for(int i = 0;i< nums.size()-1;i++)
             // {
             //     for(int j = i+1;j< nums.size();j++)
             //     {
             //         if(nums[i] + nums[j] == target)
             //             return vector<int>{i,j};
             //     }
             // } 
             // return vector<int>{};
     //哈希
             unordered_map<int,int>umap;
             for(int i =0;i< nums.size();i++)
             {
                 umap.emplace(nums[i],i);
             }
     ​
             for(int i = 0;i< nums.size();i++)
             {
                 if( umap.find( target - nums[i] ) != umap.end() && i != umap[target-nums[i]] )   // 有可能是nums[i] 是target的一半
                 {
                     //printf("%d %d %d \n",i,umap[target-nums[i]],target-nums[i]);
                     return vector<int>{i, umap[target-nums[i]] };
                 }
             }
             //printf("31\n");
             return vector<int>{};
         }

  • 先将所有的值加入哈希,有可能找到自身,所以需要额外处理

  •  
    vector<int> twoSum(vector<int>& nums, int target) {
             unordered_map<int,int>umap;
            
             for(int i = 0;i< nums.size();i++)
             {
                 auto iter = umap.find(target - nums[i]);
     ​
                 if( iter != umap.end() )
                 {
                     return {i, iter->second };//vector<int>{i, iter->second };
                 }
                 umap.emplace(nums[i],i);
             }
         
             return {};
         }

  • 边遍历边加入, 就避免了找到自身

454. 四数相加 II

  • 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

    0 <= i, j, k, l < n nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

  •  
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
             unordered_map<int,int>summap;
             int result = 0;
             for(auto  i :nums1)
             {
                 for(auto j:nums2)
                 {
                     summap[i+j]++;   //记录前俩数组和以及出现次数
                 }
             }
     ​
             for(auto  i :nums3)
             {
                 for(auto j:nums4)
                 {
                     auto iter = summap.find(-(i+j));
                     if( iter != summap.end() )
                     {
                         result += summap[-(i+j)];
                     } 
                 }
             }
             return result;
         }

  • 先记录前俩数组和以及出现次数

15. 三数之和

  • 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

    你返回所有和为 0 且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

  • 示例 1:

     输入:nums = [-1,0,1,2,-1,-4]
     输出:[[-1,-1,2],[-1,0,1]]
     解释:
     nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
     nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
     nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
     不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
     注意,输出的顺序和三元组的顺序并不重要。

    示例 2:

     输入:nums = [0,1,1]
     输出:[]
     解释:唯一可能的三元组和不为 0 。

    示例 3:

     输入:nums = [0,0,0]
     输出:[[0,0,0]]
     解释:唯一可能的三元组和为 0 。
  • vector<vector<int>> threeSum(vector<int>& nums) {
             sort(nums.begin(),nums.end());
             vector<vector<int>>result;
     ​
             multimap<int,int>mmap;
             for(int i = nums.size()-1;i>=0;i--)
             {
                 mmap.emplace(nums[i],i);
             }
     ​
            for(int i =0;i< nums.size();i++)
            {
                if(nums[i] > 0) //第一个数
                     break;
                 if(i>0 && nums[i] == nums[i-1])        // 三元组a去重
                     continue;
     ​
                 for(int j = i+1;j<nums.size();j++)
                 {
                     if(j> i+1 && nums[j] == nums[j-1])  //三元组b去重
                         continue;
                     
                     int c = -(nums[i]  + nums[j]);
                     auto iter = mmap.find(c);
                     if( iter != mmap.end()  && iter->second > j)//要保证找的c在a b之后
                     {
                         result.push_back({nums[i],nums[j],c});
                     } 
                     
                 }
            }
             return result;
         }
  • 去重很麻烦,a b的去重可以统一起来

  • 对于c一定保证是位于a b 之后的 所以可以从后往前找

  • vector<vector<int>> threeSum(vector<int>& nums) {
             vector<vector<int>> result;
             sort(nums.begin(),nums.end());
             //三个数 nums[i] + nums[left] + nums[right]
             for(int i = 0;i<nums.size();i++)
             {
     // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
                 if(nums[i] > 0 ) return result;
     ​
                 if(i > 0 && nums[i] == nums[i-1])
                     continue;
                 int left = i+1;
                 int right = nums.size()-1;
     ​
                 while(left < right)
                 {
                     //去重逻辑
                     if(right <nums.size()-1 &&  nums[right] == nums[right+1])
                     {
                         right--; continue;
                     } 
                     if(left > i+1 && nums[left] == nums[left-1])
                     {
                         left++; continue;
                     } 
     ​
                     if( nums[i] + nums[left] + nums[right] > 0)
                         right--;
                     else if(nums[i] + nums[left] + nums[right] < 0)
                         left++;
                     else{
                         result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                         // 找到答案时,双指针同时收缩
                         right--;
                         left++;
                         //while(right> left && nums[right] ==)
                     }
                 }
                 
             }
             return result;
         }

  • 双指针的移动 : 先固定第一个数, 剩下的两个数采用双指针

  • 去重逻辑保持一致 : 去重是先使用之后再去重

  • 双指针适用于 排好序的数组,如果没有排好序且要求下标则不适合双指针

  • 定一找二

  • 本题的哈希表只能是三重for ,然后用哈希表去找剩下的一个

18. 四数之和

  • 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

    • 0 <= a, b, c, d < n

    • abcd 互不相同

    • nums[a] + nums[b] + nums[c] + nums[d] == target

    你可以按 任意顺序 返回答案 。

    示例 1:

     输入:nums = [1,0,-1,0,-2,2], target = 0
     输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

    示例 2:

     输入:nums = [2,2,2,2,2], target = 8
     输出:[[2,2,2,2]]
  •  
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
             if(nums.size() < 4) return {};
             vector<vector<int>>result;
     ​
             sort(nums.begin(),nums.end());
     ​
             for(int i = 0;i< nums.size();i++)
             {
                 if(nums[i]> target && nums[i] > 0) break; // 剪枝处理
     ​
                 if(i> 0 && nums[i] == nums[i-1]) continue; //去重
     ​
                 for(int j = i+1;j< nums.size();j++)
                 {
                     if(nums[i] + nums[j] > target && nums[i] + nums[j] >0) break; // 剪枝处理
     ​
                     if(j> i+1 && nums[j] == nums[j-1]) continue; //去重
     ​
                     long sum = (long)target - nums[i] - nums[j];
                     int left = j+1,right = nums.size()-1;
     ​
                     while(left < right)
                     {
                         if(right< nums.size()-1 && nums[right] == nums[right+1])  //去重
                         {
                             right--;continue;
                         }
                         if(left > j+1 && nums[left] == nums[left-1])   //去重
                         {
                             left++;continue;
                         }
     ​
                         if( nums[left] + nums[right] > sum)
                             right--;
                         else if(nums[left] + nums[right] < sum)
                             left++;
                         else{
                            result.push_back({nums[i],nums[j],nums[left],nums[right]});
                             left++;
                             right--;
                         }
                     }
     ​
                 }
             }
             return result;
         }

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值