• 算法思想总结:哈希表


    一、哈希表剖析

    1、哈希表底层:通过对C++的学习,我们知道STL中哈希表底层是用的链地址法封装的开散列

    2、哈希表作用:存储数据的容器,插入、删除、搜索的时间复杂度都是O(1),无序。

    3、什么时候使用哈希表:需要频繁查找数据的场景。

    4、OJ中如何使用哈希表???

    (1)STL中的容器(适用所有场景,比如字符串相关、数据映射下标)

    (2)数组模拟简易哈希表(减小时间损耗,容器的封装有一定代价)—>大多以下两种情况适用

    情况1:(char)涉及到字符串中的“字符” ,hash[26]可以映射所有的字母。

    情况2:(int)数据范围较小的时候

    二、两数之和

    . - 力扣(LeetCode)

    解法2代码: 

    1. class Solution {
    2. public:
    3. vector<int> twoSum(vector<int>& nums, int target)
    4. {
    5. unordered_map<int,int> hash; //数值和下标的映射关系
    6. int n=nums.size();
    7. for(int i=0;i
    8. {
    9. int x=target-nums[i];
    10. if(hash.count(x)) return {hash[x],i};
    11. hash[nums[i]]=i;
    12. }
    13. return {-1,-1};
    14. }
    15. };

     三、判定是否互为字符重排

    . - 力扣(LeetCode)

     解法2代码:

    1. class Solution {
    2. public:
    3. bool CheckPermutation(string s1, string s2)
    4. {
    5. //小优化
    6. if(s1.size()!=s2.size()) return false;
    7. //用哈希表
    8. int hash[26]={0};
    9. for(char&ch:s1) ++hash[ch-'a'];
    10. //检测第二个数组
    11. for(char&ch:s2) if(--hash[ch-'a']<0) return false;
    12. return true;
    13. }
    14. };

    四、存在重复元素I

    . - 力扣(LeetCode)

    解法2代码:

    1. class Solution {
    2. public:
    3. bool containsDuplicate(vector<int>& nums) {
    4. unordered_set<int> hash; //有负数,所以必须用库里面的哈希表
    5. for(auto&e:nums)
    6. if(hash.count(e)) return true;
    7. else hash.insert(e);
    8. return false;
    9. }
    10. };

     五、存在重复元素II

    . - 力扣(LeetCode)

    解法1代码:

    1. class Solution {
    2. public:
    3. bool containsNearbyDuplicate(vector<int>& nums, int k)
    4. {
    5. unordered_map<int,size_t> hash;//数值和下标的映射
    6. size_t n=nums.size();
    7. for(size_t i=0;i
    8. {
    9. //如果哈希表中有这个数
    10. if(hash.count(nums[i])&&i-hash[nums[i]]<=k) return true;
    11. hash[nums[i]]=i;//存下标的映射
    12. }
    13. return false;
    14. }
    15. };

    解法2代码:

    1. class Solution {
    2. public:
    3. bool containsNearbyDuplicate(vector<int>& nums, int k) {
    4. //滑动窗口解题,让set始终保存着k个元素,如果发现了区间内有重复元素,那么就可以返回true
    5. unordered_set<int> s;
    6. size_t n=nums.size();
    7. for(size_t i=0;i
    8. {
    9. if(s.count(nums[i])) return true;
    10. s.emplace(nums[i]);//不断将数字丢进去
    11. if(i>=k) s.erase(nums[i-k]); //窗口超出区间了,就将最前面那个给删掉
    12. }
    13. return false;
    14. }
    15. };

    六、存在重复元素III(经典)

    . - 力扣(LeetCode)

     解法1代码:

    1. class Solution {
    2. public:
    3. //绝对值尽量拆解掉
    4. //滑动窗口解决问题(控制区间) 需要支持插入、查找、删除 尽可能有序 set
    5. //k是下标的差值 t是元素的差值
    6. bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t)
    7. {
    8. //lower_bound 利用二分找到第一个>=num的迭代器 降序就是<=
    9. //upper_bound 利用二分找到第一个>num的迭代器 降序就是<
    10. set<long> s;//需要一个有序集合
    11. for(size_t i=0;isize();++i)
    12. {
    13. //在下标范围为 [max(0,i−k),i] 内找到值范围在 [u−t,u+t]的数。
    14. set<long>::iterator it=s.lower_bound((long)nums[i]-t);
    15. if(it!=s.end()&&*it<=(long)nums[i]+t) return true;
    16. s.insert(nums[i]);
    17. if(i>=k) s.erase(nums[i - k]);
    18. }
    19. return false;
    20. }
    21. };

     思路2:分桶(非常精巧的思路)

    思路来源:. - 力扣(LeetCode)分桶思路详细讲解

         因为这个思路来源写得非常的详细,所以直接看就行,以往我们的分桶,更多的是针对整数的分桶,但是在该题中,扩展了存在负数的时候如何分桶,保证每个桶内的元素个数是一样的。这是一种非常巧妙的方法!!!要结合具体的实例去看!!

    核心思路:保证每个桶内的绝对值相差小于t,k个桶。当我们遍历到这个数的时候,如果对应的桶的存在,就是true,如果相邻桶存在,看看差值是否符合要求。每个桶中只会有一个元素,因为有多的我们就会直接返回结果。

    1. class Solution {
    2. public:
    3. int getid(long u,long t)
    4. {
    5. return u>=0?u/(t+1):(u+1)/(t+1)-1;
    6. }
    7. bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t)
    8. {
    9. //桶排序
    10. size_t n=nums.size();
    11. //分成k个桶 每个桶的大小是t+1 (0,1,2,3) ->保证一个桶里面是符合的
    12. unordered_map<int,int> hash; //第一个是存id 第二个是存元素
    13. for(size_t i=0;i
    14. {
    15. long u=nums[i];
    16. int id= getid(u,t); //找编号
    17. //看看当前桶存不存在
    18. if(hash.count(id)) return true;
    19. //看看相邻桶在不在,如果在的话 就看看差值
    20. if( hash.count(id-1)&&u-hash[id-1]<=t
    21. ||hash.count(id+1)&&hash[id+1]-u<=t) return true;
    22. if(i>=k) hash.erase(getid(nums[i-k],t));//桶数多了,就去掉一个
    23. hash[id]=u;//开新的桶
    24. }
    25. return false;
    26. }
    27. };

    七、字母异位词分组(经典)

    . - 力扣(LeetCode)

    1. class Solution {
    2. public:
    3. vector> groupAnagrams(vector& strs) {
    4. //字母异位词->排序后都是相同的
    5. unordered_map> hash; //将异位词绑定起来
    6. for(auto&s:strs)
    7. {
    8. string temp=s;
    9. sort(temp.begin(),temp.end());
    10. hash[temp].emplace_back(s);
    11. }
    12. //提取出来
    13. vector> ret;
    14. for(auto&[x,y]:hash) ret.emplace_back(y); //取哈希表中键值对的方法C++14支持
    15. //for(auto&kv:hash) ret.push_back(kv.second);
    16. return ret;
    17. }
    18. };

    八、前K个高频单词(经典)

     . - 力扣(LeetCode)

    解法1:map+vector+稳定排序+lambda优化
              map的底层是红黑树,插入的时候map 会按照字典序排好,而我们现在要按照出现次序去排序,同时对于出现次数相同的保证字典序在前面,所以我们其中之一的策略就是vector+sort ,但是sort底层是快排,并不具备稳定性,但是库里面有一个stable_sort是稳定的排序

    1. class Solution {
    2. public:
    3. typedef pairint> PSI;
    4. vector topKFrequent(vector& words, int k)
    5. {
    6. mapint> countmap;//计数
    7. for(auto&s:words) ++countmap[s];
    8. //此时已经按照字典序排好了,将其拷贝到vector中
    9. vector nums(countmap.begin(),countmap.end());
    10. //要用一个稳定的排序 我们排序的是比较value,所以要修改比较逻辑
    11. stable_sort(nums.begin(),nums.end(),
    12. [](const PSI&kv1,const PSI&kv2) {return kv1.second>kv2.second;});
    13. vector ret(k);
    14. for(int i=0;i
    15. return ret;
    16. }
    17. };

    解法2:unordered_map+vector+sort+调整比较逻辑+lambda优化 

    1. class Solution {
    2. public:
    3. typedef pairint> PSI;
    4. vector topKFrequent(vector& words, int k)
    5. {
    6. unordered_mapint> countmap;//计数
    7. for(auto&s:words) ++countmap[s];
    8. //此时已经按照字典序排好了,将其拷贝到vector中
    9. vector nums(countmap.begin(),countmap.end());
    10. //要用一个稳定的排序 我们排序的是比较value,所以要修改比较逻辑
    11. sort(nums.begin(),nums.end(),
    12. [](const PSI&kv1,const PSI&kv2){
    13. return kv1.second>kv2.second||(kv1.second==kv2.second&&kv1.first
    14. vector ret(k);
    15. for(int i=0;i
    16. return ret;
    17. }
    18. };

    上面两种解法都是借助vector容器+sort去解决的。

     解法3:unordered_map+priority_queue+compare类

    1. class Solution {
    2. public:
    3. typedef pairint> PSI;
    4. struct compare//要注意仿函数要+const修饰,否则可能编译不过
    5. {
    6. bool operator()(const PSI&kv1,const PSI&kv2) const
    7. {
    8. if(kv1.second==kv2.second) return kv1.first
    9. return kv1.second>kv2.second;
    10. }
    11. };
    12. vector topKFrequent(vector& words, int k)
    13. {
    14. unordered_mapint> countmap;//计数
    15. for(auto&s:words) ++countmap[s];
    16. //丢到优先级队列里
    17. priority_queue,compare> heap;
    18. for (auto& it : countmap) {
    19. heap.push(it);
    20. if (heap.size() > k) heap.pop();
    21. }
    22. vector ret(k);
    23. for(int i=k-1;i>=0;--i)
    24. {
    25. ret[i]=heap.top().first;
    26. heap.pop();
    27. }
    28. return ret;
    29. }
    30. };

     解法4:unordered_map+multiset+compare类

    1. class Solution {
    2. public:
    3. typedef pairint> PSI;
    4. struct compare//要注意仿函数要+const修饰,否则可能编译不过
    5. {
    6. bool operator()(const PSI&kv1,const PSI&kv2) const
    7. {
    8. return kv1.second>kv2.second||(kv1.second==kv2.second&&kv1.first
    9. }
    10. };
    11. vector topKFrequent(vector& words, int k)
    12. {
    13. unordered_mapint> countmap;//计数
    14. for(auto&s:words) ++countmap[s];
    15. multiset sortmap(countmap.begin(),countmap.end());
    16. vector ret(k);
    17. multiset::iterator it=sortmap.begin();
    18. size_t i=0;
    19. while(k--)
    20. {
    21. ret[i++]=it->first;
    22. ++it;
    23. }
    24. return ret;
    25. }
    26. };

     解法5:map+multimap+compare类(能过 但这是巧合)

           这题能过的原因是map实现了字典序的排序。而底层的multimap封装中对于相同的键值是优先插入到其右侧。

    1. class Solution {
    2. public:
    3. struct compare//要注意仿函数要+const修饰,否则可能编译不过
    4. {
    5. bool operator()(const int&k1,const int&k2) const
    6. {
    7. return k1>k2;
    8. }
    9. };
    10. vector topKFrequent(vector& words, int k)
    11. {
    12. mapint> countmap;//计数
    13. for(auto&s:words) ++countmap[s];
    14. multimap<int,string,compare> sortmap;
    15. for(auto &kv:countmap) sortmap.insert(make_pair(kv.second,kv.first));
    16. vector ret(k);
    17. auto it=sortmap.begin();
    18. size_t i=0;
    19. while(k--)
    20. {
    21. ret[i++]=it->second;
    22. ++it;
    23. }
    24. return ret;
    25. }
    26. };

  • 相关阅读:
    flutter 身兼数职的getx —— 简介
    【深度学习21天学习挑战赛】9、生成对抗网络(GAN)手写数字生成
    操作系统基础知识
    Java基于JSP旅游网站系统的设计于实现
    最新发现:《羊了个羊》通关靠运气,项目通关靠双商
    rancher 导入k8s集群
    SAP 采购发票校验之 后续贷记 MIRO <转载>
    Linux---Socket
    Windows网络管理及诊断命令整理
    【HarmonyOS】鸿蒙轻量级智能穿戴应用可以集成华为分析SDK吗?
  • 原文地址:https://blog.csdn.net/weixin_51142926/article/details/139162692