哈希表定义
散列表(Hash table,哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表
看不懂,问题不大
哈希表主要是以空间换时间在查找元素时比数组遍历快很多
哈希:就是将有效的字符转换成有限或者固定的数字,不过哈希与加密不同,加密转化可逆,但是哈希转化不可逆;
哈希表:哈希表与数组不同但是有些地方相似,如数组利用索引确定数据位置,但是哈希表不再直接使用索引而是使用键值对进行表示 ,哈希表很像一种高级数组,但是数组的索引值一定是一个整数,通过这个整数找到对应内存地址,而哈希表的键可以是字符串或者整数,这个键不直接对应相应内存地址,需要通过运算后得到的数字才可以找到对应的内存地址,运算就是哈希函数,哈希函数后面有解释
同参数->同整数
不同参数->不同整数
哈希函数
上面讲到哈希函数负责把键转换为一个整数,这里举一个例子把字符串哈希
设置一个可以存5个键值对的哈希表,现在往里面放字符串,可把每个字符的ascll值相加对5取余,这样就可以把字符串转化为整数,这个过程就可以看做是一个哈希函数,但是这里有一个严重的问题就是有两个或者多个字符串转化后数字相同该如何处理,这个问题在哈希里面几乎是必然出现的,我们把这些问题叫做冲突
解决冲突的方法
解决冲突的方法主要有俩种方法开放寻址法和封闭寻址法
开放寻址法
线性探查法是开放寻址法的一种,若有数据在添加时与前面的数据发生位置冲突,可以探查下一个位置是否被占用,重复操作,直到将该数据存入,在查找时可以找到键相对的位置开始探查若未找到,则向下探查。
就是当数据过多时,线性探查法在某一区域位置可能被占完,事实上在哈希表被占位置较多时,发生冲突的概率将会提升,在这时哈希表就应该进行扩容还拿HashMap来说,当它当前的容量占总容量的百分之七十五的时候就需要扩容了。而且这个扩容也不是简单的把数组扩大,而是新创建一个数组是原来的2倍,然后把原数组的所有键都重新Hash一遍放到新的数组。
封闭寻址法
链表法是封闭寻址法的一种,就是将哈希表的位置设为链表,若有数据在添加时与前面的数据发生位置冲突,则可以把该数据存如相应节点,在查找的时候可以在该位置下进行遍历,可以结合上面那个图进行理解,
就是当数据过多时,链表法在探查数据遍历链表时耗过长,拿java集合类中的HashMap来说吧,如果这里的链表长度大于等于8的话,链表就会转换成树结构,当然如果长度小于等于6的话,就会还原链表
例题
1.给你一个整数数组 nums ,它包含 2 * n 个整数。
你需要将 nums 划分成 n 个数对,满足:
每个元素 只属于一个 数对。
同一数对中的元素 相等 。
如果可以将 nums 划分成 n 个数对,请你返回 true ,否则返回 false 。
来源:力扣(LeetCode) 2206
思路:定义一个数组作为哈希数组,遍历数组并将哈希表对应位置进行自增,然后遍历哈希数组发现奇数则不符合,全部为偶数则符合
bool divideArray(int* a, int n){
int b[501];
for(int i=0;i<501;i++)
{
b[i]=0;
}
for(int i=0;i<n;i++)
{
b[a[i]]++;
}
for(int i=0;i<501;i++)
{
if(b[i]&1==1)
return false;
}
return true;
}
2.全字母句 指包含英语字母表中每个字母至少一次的句子。
给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句 。
如果是,返回 true ;否则,返回 false 。
来源:力扣(LeetCode)1832
思路:可以将每一个字母转换为ascll值存入一个数组,完成后遍历数组,判断在a到z的ascll之间是否有数组元素为0;
bool checkIfPangram(char * b){
int a[256],n=strlen(b);
for(int i=0;i<256;i++)
{
a[i]=0;
}
for(int i=0;i<n;i++)
{
a[b[i]]++;
}
for(int i='a';i<='z';i++)
{
if(a[i]==0)
return false;
}
return true;
}
3.给你一个整数数组 nums 。
如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。
返回好数对的数目。
来源:力扣(LeetCode) 1512
思路:定义一个哈希数组,一个变量记录数对个数,遍历原数组nums,在经过其中某个元素时,添加该元素相应哈希数组元素(遍历到一个元素求其可以组成数独个数,就是求他前面与该元素相同的值的出现次数);
int numIdenticalPairs(int*a, int n){
int b[101];
for(int i=0;i<101;i++)
{
b[i]=0;
}
int ans=0;
for(int i=0;i<n;i++)
{
ans+=b[a[i]];
b[a[i]]++;
}
return ans;
}
4.给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。
来源:力扣(LeetCode)930
思路:求子区间和是否与goal相同,问题在于如何快速求出子区间和,可以利用前缀和求子区间和,l到r 的区间和t=s[r]-s[l-1] (l>=0), 则s[r]-s[l]=t,就是l+1到r的区间和,s[r]=t+s[l] (l>=-1) ;这时是s [ r ](r >=-1 )就是符合条件的.
int numSubarraysWithSum(int* a, int n, int m){
for(int i=1;i<n;i++)
{
a[i]=a[i]+a[i-1];
}
int ans=0;
int b[40000]={0};
// b[a[-1]+goal]++;a[-1]越界,默认为0,0不影响后续计算
b[m]++;
for(int i=0;i<n;i++)
{
ans+=b[a[i]];
b[a[i]+m]++;
}
return ans;
}
这些都是哈希表的简单应用,要掌握哈希表还要多写题