哈希表原理

1.哈希表是什么

     哈希表(hash table,又叫散列表),其原理的核心就是哈希函数(即散列函数),即如何构建一个确定的映射,它能把关键字映射到一个唯一的存储位置,如下公式所示

记录的存储位置=f(关键字)

f为散列函数,又称哈希函数。通过此函数将值映射到表中的一个位置来访问记录,以加快查找的速度,存放记录的数组叫做散列表。

2.Hash表的基本思路

假设ki为某个对象的关键字,则用哈希函数h(ki)来映射ki的内存地址,也就是ki的下标,将ki对象的元素内容存入这个地址中就行了。

用下面一个通俗易懂的例子来解释

现在需要存储4个元素 13 7 14 11

显然,我们可以用数组来存。也就是:a[0]=13;a[1]=7;a[2]=14,a[3] = 11

我们也可以用hash表来存储

先确定哈希函数为h(ki)=ki%5;,因此

对于第一个元素h(13)=13%5 = 3;也就是说13的下标为3;即Hash[3]=13;

对于第二个元素h(7)=7%5=2;也就是说7的下标为2;即Hash[7]=2

以此类推 Hash[4]=14;Hash[1]=11.

咋一看这样并没有什么优势,其实不然,实际上其在查找方面效率很高

比如现在我要查找11这个元素是否存在,如果用数组,遍历一遍,即需要查找4次,时间复杂度为O(n)

而用哈希函数来实现,代入上面的公式有

h(11) = 11%5=1,即查找了1次,就找到了此值,时间复杂度为O(1)

3.哈希表优缺点

优点:一对一的查找效率很高;

缺点:一个关键字可能对应多个散列地址;需要查找一个范围时,效果不好。

 


4.哈希冲突问题

所谓哈希冲突即一个关键字映射到多个值得情况,如

10%5=0;20%5=0,即Hash[0]=10 还是 Hash[0]=20呢

解决哈希冲突常见的方法有拉链法,开放地址法,再散列法

4.1 拉链法

拉链法即数组和链表的结合,如下图所示:

哈希表原理

左边为一个数组,数组中成员为一个指针,指向一个链表的头

比如要寻找353,首先找到数组下标为1的位置,再找到值为1的链表,此时由于(1不等于363),,继续往下查找下一个链表(337不等于353),再查找下一个链表,即找到该值。

对于要插入存储新的值,在冲突的时候,则将值存在该行链表的最右边.。

位桶:上述图中每一个数组对应的所有链表即在一个位桶之中

哈希桶:哈希桶就是盛放不同key链表的容器(即是哈希表)

装载因子:即存储的关键字/哈希表中可以散列的位置的大小,值越大表示hash表越拥挤,越容易引起冲突

优缺点

拉链法的优点在于处理冲突简单,且由于用到了链表,删除方便。

缺点是表长不固定,需要额外的的空间。

3.2 开放地址法

开放地址法的特征就是,每个位桶中没有链表,只会存储一个值,即每个位桶的装载因子等于1。

它的实现是在插入一个元素的时候,通过哈希函数判断,如果发生冲突,就以当前地址为基准,再寻址去寻找下一个地址,若发生冲突再去寻找下下个地址,直到找到一个空的地址为止。

(1)线性探查法

dii=1,2,3,…,m-1;这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

(2)二次探查

di=12,-12,22,-22,…,k2,-k2    ( k<=m/2 );这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。

(3)伪随机探查

在查找位置index周围随机的查找。称为随机在探测。

di=伪随机数序列;具体实现时,应建立一个伪随机数发生器,(如i=(i+p) % m),生成一个伪随机序列,并给定一个随机数做起点,每次去加上这个伪随机数就可以了。

3.3 再散列法

再散列法其实很简单,就是在使用哈希函数去散列一个输入的时候,输出是同一个位置就再次散列,直至不发生冲突位置

缺点:每次冲突都要重新散列,计算时间增加。

参考资料

https://blog.****.net/qq_41230365/article/details/81058217

https://blog.****.net/qq_32595453/article/details/80660676

https://blog.****.net/yyyljw/article/details/80903391