哈希表是我们经常频繁使用的数据结构,所以它的知识点比较重要,如HashMap啊,就是哈希表结构,哈希表的底层是数组+链表结构的,非常之聪明,两者优点结合,数组查询快,链表增删快,并且hash采用算法分析定位地址,而不用再像数组一样需要遍历。
答:通过一定算法计算出来的数字就可以对应数组下标找到对应位置,假如现在计算完的数字是0需要定位到0坐标上,然后又有一个数据需要存储,计算完之后位置还是0,那现在怎么办呢,那也不能覆盖把,这种问题的出现也叫哈希碰撞,那现在解决办法就是加链表,当发现计算完的位置是一样的就存储在一连串的链表里就能实现。
我就不瞎巴巴了,还是上官方介绍哈希表的介绍把
哈希表(hash table,也叫散列表)是根据关键码值(key,value)而直接进行访问的数据结构。也就是说它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表可以提供快速的插入和查找工作,哈希表运算的非常快,而且编程实现也比较容易。哈希表是数组和链表结构
记录的存储位置=f(关键字),这里的对应关系f称为散列函数,又称为哈希(哈希函数),采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表
--视频资料介绍图片
由于存储空间有限,hash计算以后可能不同的关键字映射到同一个哈希地址上,这种现象称之为哈希冲突,比如h(16)=16%11=5,h(27)=27%11=5,两个哈希地址就冲突了
--视频资料介绍图片
hash冲突是不可避免的,要解决hash冲突需要以下几种方式
哈希冲突解决策略:开放寻址法(将所有的元素都存放在哈希表内的数组中,不使用额外的数据结构)
开放寻址法最简单的一种实现就是线性探查,步骤如下:
视频资料图
线性探查(Linear Probing)方式虽然简单,但并不是解决冲突的最好的策略,因为会导致同类hash的聚集,这导致搜索hash表时冲突依然存在,例如上图例子中的哈希表,如果我们要访问Edward的信息,因为Edward的社保号是111-00-1235 哈希为1235,然而我们在1235找到的位置是Bob,所以再搜索1236,找到的确是Danny,以此类推才能找到最终答案,所以在查找的时候也会出现冲突的。
开放地址法的线性探测缺点就是,数据项聚集,越来越大,越到后面找到的空位插入数据项需要时间越多。也就是说当哈希表插入的数据越来越多的时候,探测的长度就非常长,后续的插入操作也非常耗时。
线性探测就是使用算术取余的方法计算余数,当产生冲突时就通过线性递增的方法进行探测,一直到数组的位置为空,插入数据项即可。
网图
以上是通过一个长度是10的数组进行hash存储。对数据10 20 30 23 24 25 35 37 38 9进行储存,数组长度为10;那么当第一个10%10=0,即把10放入HashArray[0]位置上,当对下一个数字20进行存储时,计算20%10=0,此时HashArray[0]已经有数据了,其实通过线性探测对HashArray[1]进行探测。此时HashArray[1]null,将数据20插入该位置,其他数据插入同理。
你已经猜到了,一个知识点第一个讲到的总是有缺陷,需要弥补的,那么二次探查和二度哈希出现在江湖。
一种改进的方式为二次探查(Quadratic Probing),即每次检查位置空间的步长为平方倍数,也就是说,如果位置s被占用,则首先检查s+1的平方,然后检查s-1的平方,s+2的平方,s-2的平方,依次类推而不是像线性探查那样s+1,s+2.....方式增长,尽管如此,二次探查同样也会导致同类哈希聚集问题。
二度哈希是把关键字用不同的的哈希函数再做一遍哈希化,用这个结果作为步长,对指定的关键字,探测的步长是不变的,可以说不同的关键字可以使用不同的步长,并且步长可以控制。一般来说,再哈希函数可以采用以下公式
stepSize=constant-(key%constant);
以上是使用地址开放的策略以及关于地址开放的三种探测方法,探测序列通常使用再哈希法生成。
前面我们谈到了散列冲突处理的开放地址法,它的思路就是一旦发生了冲突,就去寻找下一个空的散列地址,那么,为什么有冲突就非要换地方呢,我们就在原地处理行不行呢?可以这样处理,这样才有的链地址法。
视频学习拉链法图
网图
将所有关键字为同义词的记录存储在一个单链表中,我们称这种表为同义词子表,在散列表中只存储所有同义词子表的头指针。
此时,已经不存在什么冲突换址的问题,无论有多少个冲突,都只是在当前位置给单链表增加结点的问题。很不错的解决思路吧?
与开放地址法相比,拉链法有如下几个优点!
拉链法的缺点:指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度
总结:链地址法的优势是对于可能会造成很多冲突的散列函数来说,提供了绝不会出现找不到地址的保障。当然,这也就带来了査找时需要遍历单链表的性能损耗,不过性能损耗在很多场合下也不是什么大问题。
一种好的哈希做法是以独立于数据中可能存在的任何模式的方式导出哈希值。例如:除法哈希法用一个特定的质数来除所给的关键字,所得的余数即为该关键字的哈希值,除法哈希函数可表示为:
hash(key)=key mod m
其中key表示被哈希的关键字,m表示哈希表的大小,mod取余操作。假定所选择的质数与关键字分布中的任何模式都是无关的,这种方法常常可以给出很好的结果。