哈希表详解
最近在做负荷分担的优化,将数据流均匀分到八条流中,学习点哈希算法
什么是哈希表?
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。说白了哈希表的原理其实就是通过空间换取时间的做法。。
哈希表的做法其实很简单,就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。。
哈希函数的构造方法:
若对于关键字集合中的任一个关键字,哈希函数映像到地址集合中任何一个地址的概率是相等的,则称此类哈希函数为均匀的哈希函数。换句话说,就是使关键字经过哈希函数得到一个“随机的地址“,以便使一组关键字的哈希地址均匀分布在整个地址区间中,从而减少冲突。
(1)直接定址法
取关键字或关键字的某个线性函数值为哈希地址。即: H(key)=key或H(key)=a*key+b; 其中a和b为常数(这种哈希函数叫做自身函数)。
由于直接定址所得地址集合和关键字集合的大小相同。因此,对于不同的关键字不会发生冲突。但实际中使用这种哈希函数的情况很少。
(2)数字分析法
假设关键字是以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。
(3)平方取中法
取关键字平方后的中间几位为哈希地址。
(4)斐波那契(Fibonacci)散列法
平方散列法的缺点是显而易见的,所以我们能不能找出一个理想的乘数,而不是拿value本身当作乘数呢?答案是肯定的。
1)对于16位整数而言,这个乘数是40503
2)对于32位整数而言,这个乘数是2654435769
3)对于64位整数而言,这个乘数是11400714819323198485
这几个“理想乘数”是如何得出来的呢?这跟一个法则有关,叫黄金分割法则,而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,即如此形式的序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,…。另外,斐波那契数列的值和太阳系八大行星的轨道半径的比例出奇吻合。
对我们常见的32位整数而言,公式: index = (value * 2654435769) >> 28
(5)折叠法
将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。
(6)除留余数法(==最常用的方法)
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。即H(key)=key MOD p,(p<=m),这是一种最简单,也是最常用的构造哈希函数的方法。它不仅可以对关键字直接取模(MOD),也可在折叠、平方取中等运算之后取模。
(7)随机数法
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)=random(key),其中random为随机函数。通常,当关键字长度不等时采用此法构造哈希函数较切当。
总结:
实际工作中需视不同情况采用不同的哈希函数,通常,考虑的因素有:
(1)计算哈希函数所需时间(包括硬件指令的因素);
(2)关键字的长度;
(3)哈希表的大小;
(4)关键字的分布情况;
(5)记录的查找频率。
处理冲突的方法(冲突只能减少,不能避免):
(1)开放定址法
(2)再哈希法
(3)链地址法
(4)建立一个公共溢出区
哈希表的查找及其分析:
在哈希表上进行查找的过程和哈希造表的过程基本一致。给定的K值,根据造表时设定的哈希函数求得哈希地址,若表中此位置上没有记录,则查找不成功;否则必将关键字,若和给定的值相等,则查找成功;否则根据造表时设定的处理冲突的方法找”下一地址“,直至哈希表中的某个位置为”空“或者表中所填记录的关键字等于给定值时为止。
哈希表的装填因子定义为:
α=(表中填入的记录数)/(哈希表的长度)