目录

1 哈希表理论基础

1. 1 哈希函数

1.2 哈希碰撞

2 哈希表力扣题参考代码


原文链接  代码随想录

1 哈希表理论基础

首先什么是 哈希表,哈希表(英文名字为Hash table,国内也有一些算法书籍翻译为散列表,大家看到这两个名称知道都是指hash table就可以了)。哈希表是根据关键码的值而直接进行访问的数据结构。这么这官方的解释可能有点懵,其实直白来讲其实数组就是一张哈希表。哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,如下图所示:

那么哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现集合里。例如要查询一个名字是否在这所学校里。要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数

1. 1 哈希函数

哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。

如果hashCode得到的数值大于 哈希表的大小了,也就是大于tableSize了,怎么办呢?此时为了保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做一个取模的操作,就要我们就保证了学生姓名一定可以映射到哈希表上了。此时问题又来了,哈希表我们刚刚说过,就是一个数组。如果学生的数量大于哈希表的大小怎么办,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表 同一个索引下标的位置。接下来哈希碰撞登场

1.2 哈希碰撞

如图所示,小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞

一般哈希碰撞有两种解决方法, 拉链法和线性探测法。

1.2.1 拉链法

刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了

(数据规模是dataSize, 哈希表的大小为tableSize)其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

1.2.2 线性探测法

使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示:

1.3 常见的三种哈希结构

当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。

  • 数组
  • set (集合)
  • map(映射)

这里数组就没啥可说的了,我们来看一下set。在C++中,set 和 map 分别提供以下三种数据结构,其底层实现以及优劣如下表所示:

集合

底层实现

是否有序

数值是否可以重复

能否更改数值

查询效率

增删效率

std::set

红黑树

有序

O(log n)

O(log n)

std::multiset

红黑树

有序

O(logn)

O(logn)

std::unordered_set

哈希表

无序

O(1)

O(1)

std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。

映射

底层实现

是否有序

数值是否可以重复

能否更改数值

查询效率

增删效率

std::map

红黑树

key有序

key不可重复

key不可修改

O(logn)

O(logn)

std::multimap

红黑树

key有序

key可重复

key不可修改

O(log n)

O(log n)

std::unordered_map

哈希表

key无序

key不可重复

key不可修改

O(1)

O(1)

1.4 总结

总结一下,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!

2 哈希表力扣题参考代码

242. 有效的字母异位词

class Solution {
public:bool isAnagram(string s, string t) {if(s.size()!=t.size()){return false;}unordered_map<char,int> un_map;for(int i=0;i<s.size();i++){un_map[s[i]]++;}for(int i=0;i<t.size();i++){if(un_map.find(t[i])!= un_map.end()){if(un_map[t[i]]==0)return false;un_map[t[i]]--;}else{return false;}}return true;}
};

383. 赎金信

// 时间复杂度: O(n)
// 空间复杂度:O(1)
class Solution {
public:bool canConstruct(string ransomNote, string magazine) {int record[26] = {0};//addif (ransomNote.size() > magazine.size()) {return false;}for (int i = 0; i < magazine.length(); i++) {// 通过recode数据记录 magazine里各个字符出现次数record[magazine[i]-'a'] ++;}for (int j = 0; j < ransomNote.length(); j++) {// 遍历ransomNote,在record里对应的字符个数做--操作record[ransomNote[j]-'a']--;// 如果小于零说明ransomNote里出现的字符,magazine没有if(record[ransomNote[j]-'a'] < 0) {return false;}}return true;}
};
438. 找到字符串中所有字母异位词
class Solution {
public:vector<int> findAnagrams(string s, string p) {int s1=s.size(),p1=p.size();//两字符串大小if(s1<p1)return{};//特殊情况判断vector<int>S(26,0);//申请两数组装s,pvector<int>P(26,0);vector<int>ans;//结果数组for(int i=0;i<p1;i++)//遍历p  ,在s字符串中创建p1大小滑动窗口{S[s[i]-'a']++;P[p[i]-'a']++;//将p装入数组中,同时将s中p1个字符也装入另一数组中}if(S==P)ans.push_back(0);//两数组相同则返回子串起始索引0;for(int i=0;i<s1-p1;i++)//窗口中减少一个字符{S[s[i+p1]-'a']++;S[s[i]-'a']--;if(S==P) ans.push_back(i+1);//比较两数组相等,返回i+1;}return ans;}
};

49. 字母异位词分组

class Solution {//思路:使用哈希表,以字符串排序后作为哈希表的键,值为一维数组存排序前的字符串,//然后将哈希表的每个值存入二维数组里.
public:vector<vector<string>> groupAnagrams(vector<string>& strs) {unordered_map<string,vector<string>> un_map;vector<vector<string>> result;for(auto str:strs){string temp=str;sort(temp.begin(),temp.end());un_map[temp].push_back(str);}for(auto str:un_map){result.push_back(str.second);}return result;}
};

349. 两个数组的交集

class Solution {
public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {unordered_map<int,int> un_map1;//用两哈希表分别对两数组去从unordered_map<int,int> un_map2;vector<int> res;//结果集合for(auto num:nums1)//遍历数组1放入哈希表{un_map1[num]++;}for(auto num: nums2)//遍历数组2值放入哈希表{un_map2[num]++;}unordered_map<int,int>::iterator it=un_map1.begin();//哈希表1的迭代器for(;it!=un_map1.end();it++)//遍历哈希表1{if(un_map2.count(it->first))//如果哈希表1中键能在哈希表2中找到{res.push_back(it->first);//存入该值到哈希表中}}return res;}
};

350. 两个数组的交集 II

class Solution {
public:vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {unordered_map<int,int> un_map;vector<int> res;for(int i=0;i<nums1.size();i++)//把数组1元素存入哈希表中,值为个数{un_map[nums1[i]]++;}for(int i=0;i<nums2.size();i++)//遍历数组二{if(un_map.find(nums2[i])!=un_map.end())//该元素能在哈希表找到{if(un_map[nums2[i]]>0)//判断个数是否还有{res.push_back(nums2[i]);//返回该元素,并且在该位置哈希表值-1un_map[nums2[i]]--;}}}return res;}
};

1. 两数之和

class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int,int> un_map;//声明一个哈希表用来存遍历的数组值和下标for(int i=0;i<nums.size();i++)//遍历数组{if(un_map.find(target-nums[i])!=un_map.end())//判断是否能在哈希表中找到这个差数,找到将他们两个的下标存入数组{vector<int> vec;vec.push_back(un_map[target-nums[i]]);vec.push_back(i);return vec;}un_map[nums[i]]=i;//没有相同的就存入哈希表中}return {};}
};

202. 快乐数

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 falseif (set.find(sum) != set.end()) {return false;} else {set.insert(sum);}n = sum;}}
};

454. 四数相加 II

class Solution {
public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数// 遍历大nums1和大nums2数组,统计两个数组元素之和,和出现的次数,放到map中for (int a : nums1) {for (int b :nums2) {umap[a + b]++;}}int count = 0; // 统计a+b+c+d = 0 出现的次数// 在遍历大nums3和大nums4数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。for (int c : nums3) {for (int d : nums4) {if (umap.find(0 - (c + d)) != umap.end()) {count += umap[0 - (c + d)];}}}return count;}
};

原文链接  代码随想录

C++哈希表最详细解决相关推荐

  1. 哈希表的详细介绍 -转载

    Hashtable的定义 表示键/值对的集合,这些键/值对根据键的哈希代码进行组织. Hashtable存储结构如下 Hashtable是非泛型的集合,所以在检索和存储值类型时通常会发生装箱与拆箱的操 ...

  2. 利用哈希表和dfs解决LeetCode 399. Evaluate Division

    问题简介 给定一些由变量组成的等式组,然后根据这些等式推算出所闻的等式的结果,如果无法推算,则返回-1.0. 比如: 给定等式组 a / b = 2.0, b / c = 3.0 求出 a / c = ...

  3. 散列表(也叫哈希表),

    google搜索到的头条:散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组 ...

  4. 《我的第一本算法书》阅读笔记 1-6 哈希表原理图解

    目录 普通数组线性查找工作流程 哈希表怎么存储数据 出现冲突怎么办?(eg.链地址法) 哈希表怎么查找数据 解说 补充说明 来源 在哈希表这种数据结构中,使用将在 5-3 节讲解的"哈希函数 ...

  5. 【转载】哈希表的原理,真的很难弄懂么?

    [转载]哈希表的原理,真的很难弄懂么? 刘小爱v 发布时间:05-0909:06科技达人 转载路径: https://baijiahao.baidu.com/s?id=1666172942887109 ...

  6. python leetcode_七十五、Python | Leetcode哈希表系列

    @Author:Runsen @Date:2020/7/3 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  7. 哈希表,哈希算法(C语言)

    哈希表 哈希表,又称散列表,常用于在海量数据中查找数据 哈希表中元素是由哈希函数确定的.将数据元素的关键字key作为自变量,通过一定的函数关系H(称为哈希函数),计算出的值,即为该元素的存储地址.其优 ...

  8. SWUST OJ 1012: 哈希表(链地址法处理冲突)

    1012: 哈希表(链地址法处理冲突) 题目描述 采用除留余数法(H(key)=key %n)建立长度为n的哈希表,处理冲突用链地址法.建立链表的时候采用尾插法. 输入 第一行为哈西表的长度m: 第二 ...

  9. 两数之和(LeetCode)——哈希表法(C语言)

    上一篇文章留了个引子--用"哈希表"法来解决这个问题. 今天,我们来解决一下.为什么用哈希表法?很简单因为它--快! 讲解之前我们先来提出几个问题? 1)什么是"哈希表& ...

最新文章

  1. 来聊一聊Cookie(小甜饼),及其涉及到的web安全吧
  2. windows不能访问linux服务器,window的vnc客户端无法访问Linux服务端的问题之一
  3. 如何产生QPSK信号
  4. BZOJ 3119 Book (贪心+数学推导)
  5. 基于Maven管理的Mapreduce程序下载依赖包到LIB目录
  6. Oracle 内核参数
  7. C#中创建对象的方式
  8. pytorch —— Batch Normalization
  9. Spring Boot + Jersey发生FileNotFoundException (No such file or directory)
  10. android常用开源库分享
  11. pycharm背景图片的设置
  12. torch -index_select()、Pytorch 之修改Tensor部分值、pytorch中Tensor的数据类型
  13. 精选| 2019年5月R新包推荐(Top40)
  14. BigQuant*中金财富“启明星”创新量化交易大赛开启,月月都拿奖
  15. php-fpm优化总结
  16. android底部蒙版,Android实现蒙板效果
  17. C++使用类和对象(谭浩强9.8-9.14)
  18. 求鸡翁,鸡母,鸡雏的方案有几种,亲
  19. QT调用IE浏览器COM插件完成网页浏览
  20. 数据结构与算法JC班-左程云第一节课笔记(认识复杂度、对数器、二分法与异或运算)

热门文章

  1. Docker结合.Net Core初步使用
  2. Windows解除LongPath限制: Could not find a part of the path “xxxxx“
  3. FPGA实现除法器(verilog)
  4. 最新高清IT职业技能图谱:架构师、H5、DBA、移动、大数据、运维...
  5. HTML批量删除标记,《原神》标记全部删除如何弄 标记上限详细介绍
  6. 1年PK12年,中国式教育完败(转载)
  7. C#之txt的数据写入
  8. 直流分流器 shunt 电阻
  9. 【自动控制理论7】梅逊增益公式
  10. 计算机在化工设计中的应用论文,计算机软件技术在化工设计的应用-软件技术论文-计算机论文(9页)-原创力文档...