Hash表总结(个人面试向->可能有差错)

本人所有文章都是个人见解,希望大伙看完之后再寻找别的文章,以免出现差错

Hash表为一种数据结构,大概可以理解为值插入到哈希表之前需要进行一次映射

比较官方的说法为:散列表Hash Table,也叫哈希表),是根据关键码值(key value)而进行直接访问的数据结构。它通过关键码值映射到表中的一个位置来访问记录,以加快访问的速度。这个映射函数叫做散列函数,存放的数组叫做散列表

个人认为插入值key在进入哈希表之前要经过一次映射,映射的流程一般为经过一次取模,取模之后放入哈希表,这时候可能会出现位置被人家占了就需要用到哈希冲突,在结束哈希冲突之后如果正常那就能够存入,如果不正常空间不够那就需要触发扩容机制

所以本篇介绍内容分为

1、哈希表插入

2、哈希冲突

3、哈希扩容

4、哈希查找

5、哈希删除

哈希表插入

哈希表插入的数据为key,key的类型可以为数字、字母、字符串等还有在面试中提问过的不同对象中所包含的数据,其实这些问题都可以用一种思路去解决

存储数字很简单,只需要将数据通过取模之后初定的一个位置就确定了,这个位置还需要判断是否冲突再次变化

如果是存储字母就面临一个问题,存储数据之间的关系,以26位字母为准不考虑大小写,那减去一个a就代表a-z为0-25可以做简单映射,如果是混合大小写的字母的话就有些复杂,所以一般考虑全都化成一种

字符串的存储是比较常见的使用,一些比较长数据的存储,如下题

 可以按照层数存储位数,如第一层为第一位字母等,但如果这么做可能需要的空间十分的大,因为一层26,第二层26*26,所以这么存的话需要用链表存会比较省空间,就如C#的hashmap那样存,也有可能存储的数据比较复杂,如何映射这个时候要讲一个概念,就是哈希函数

哈希函数顾名思义就是确定哈希映射方法的函数,因为所存储的数据如果比较复杂,就用规定的几种方法来设置,这样的话开发时不需要自己再重新编写,方法分为几大类

直接定值法        直接将数据作为关键字

数字分析法        根据数据情况作为特定关键字,如时间1999.3.26逐个分配

平方取中法        取关键字平方后的中间几位数为哈希地址

折叠法        将关键字分割为相同位数的几部分,然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。

除留取余法        比较常见的将关键字取余之后得到的值为哈希地址

随机数法        选一个随机函数,取关键字的随机函数值为哈希地址,方法为:

Step1. 取出一个数据元素的关键字key,计算其在哈希表中的存储地址D=H(key)。若存储地址为D的存储空间还没有被占用,则将该数据元素存入;否则发生冲突,执行Step2。

Step2. 根据规定的冲突处理方法,计算关键字为key的数据元素之下一个存储地址。若该存储地址的存储空间没有被占用,则存入;否则继续执行Step2,直到找出一个存储空间没有被占用的存储地址为止。

哈希表在插入的时候要注意一点的是,先确定是否有合适的位置再考虑插入

其实我在面试的时候经常忽略了哈希表(散列表)意思是经过映射(哈希函数处理)之后存入值,导致我面试的时候对于字体、字符串存储没有一个概念,这点需要注意。

哈希冲突

哈希冲突是哈希函数处理完映射关系之后,两个不同的值存在同一个位置的问题。

使用的方法为:

拉链法        在冲突的地方使用链表存储不需要去别的地方

多哈希法        设计多个哈希表,在映射到同一个地址之后进行再哈希,这个方法在存储字母中文或者一些比较特殊的数字比较有用,面试的时候可能会考到。

开放地址法        开放地址则是使用计算公式计算下一步去Hi=(H(key)+di) MOD m i=1,2,...,k,有着线性探测对于d逐渐+1,直到找到空位,平方探测d可能为整或者负,随机探测d为随意值

哈希扩容

哈希扩容是在哈希表发现自身无法再继续存储数据的时候才会进行的一个机制,当然我们不会在塞满的情况下才考虑扩容,而是在到达一定百分比的时候(负载因子一般为0.75,即为75%的时候),扩容方法为两部分

  1. 创建一个新的数组,创建一个新的数组,长度newCap是原来的2倍。
  2. 遍历oldTab 取出元素的键后,重新进行hash,算出index=i = (newCap - 1) & hash
  3. 重新插入元素。

大小一般为原来的两倍,如果太小经常IO的话很消耗性能,太大可能用不到,到时再说。

哈希查找

查找的方法为使用哈希函数进行对数据进行查找,有点像哈希插入,哈希插入的方法就是先找空的地址后确认有合适的地址之后就插入,查找的时间复杂度非常小,不需要循环检测,不过如果数据是经过哈希冲突得来的那就需要对冲突结果的地址进行查找。

哈希删除

哈希删除主要针对两种,一种为线性探测,一种为开链法,因为是两种结构需要分别处理

1、线性探测:存储主要是在数组里面,删除的数据需要在位置上给予一个标记,表明空闲但被占据过,因为可能会出现被删除的位置之前被用于计算哈希冲突过。

如果想要存储k1、2、3,三个数但这三个数都映射到同个地址,如果这时删除一个k2,查找k3

 这个时候如果k3在查找时映射发现k2的位置为空,返回一个查找失败,那本次查找是有误的,因此需要在k2的位置加上一个标志,表示之前有被占据过避免查找时出现错判。可以点击这里查看对应视频

另一种结构是开链法,也就是用数组存储头节点,随后接上对应的链表,本方法的话如果有冲突则直接删除对应的子链表,但如果没有冲突(即数组地址只有一个数据链表,则将其删除并且给数组地址加上一个标志

结语

面试的时候频繁问到哈希表的问题,但每次都答得摸棱两可,因此专门写下了这篇文章,可能会有些错误,但我还是先写下以后会回来修改的,曾经我还觉得哈希表只不过是通过映射之后存储数据的结构,但多次面试之后不全面的认识给了我很多教训,希望各位多挖掘。

猜你喜欢

转载自blog.csdn.net/hoxidohanabi/article/details/127964348