一、基本概念
定义一:哈希表(Hash table,也叫散列表),是根据关键字值(key,value)直接进行访问的数据结构。也就是说,它通过把关键字映射到表中一个位置来访问的纪录,以加快查找的速度。这个映射函数叫做散列函数,存放纪录的数组叫散列表。
定义二:给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
二、常用方法
散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位。选择哈希函数考虑的因素有:计算哈希函数所需时间、关键字的长度、哈希表的大小、关键字的分布情况、记录的查找频率。
2.1 直接定址法
取关键字或关键字的某个线性函数值为哈希地址。
H(key) = key 或 H(key) = a*key+b
2.2 数字分析法
数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。
2.3 平方取中法
当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。
2.4 折叠法
将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址。数位叠加可以有移位叠加和间界叠加两种方法。移位叠加是将分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加。
2.5 随机数法
选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合。
2.6 除法散列法
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词.
H(key) = key % p (p<=m)
2.7 平方散列法
当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。
H(key) = ((key * Key) >> X) << Y
2.8 fibonacci散列法
和平方散列法类似,此种方法使用斐波那契数列的值作为乘数而不是自己。
对于16位整数而言,这个乘数是40503。
对于32位整数而言,这个乘数是2654435769。
对于64位整数而言,这个乘数是11400714819323198485。
H(key) = ((key * 2654435769) >> X) << Y
三、冲突解决
3.1 开放寻址法
开放寻址法把所有的元素都存放在散列表中,也就是每个表项包含动态集合的一个元素(元素可以为NULL)。
- 1.在开放寻址法中,当要插入一个元素时,可以连续地检查散列表的个各项(连续检查是可以通过不同的算法获得偏移位),直到找到一个空槽来放置这个元素为止。
- 2.当查找一个元素时,要检查所有的表项,直到找到所需的元素,或者最终发现元素不在表中。
- 3.在开放寻址法中,对散列表元素的删除操作执行起来比较困难。当我们从槽i中删除关键字时,不能仅将此位置元素置空。因为这样做的话,会导致在无法判断此位置是否有元素。应该用个特殊的值表示该元素已经删除。
Hi=(H(key) + di) MOD m , [i=1,2,…,k(k<=m-1)]
其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
di=1,2,3,…,m-1,称线性探测再散列。
di=1^2,-1^2,2^2,-2^2,⑶^2,…,±(k)^2,(k<=m/2)称二次探测再散列。
di=伪随机数序列,称伪随机探测再散列。
3.2 再散列法(再散列法)
产生碰撞时,再使用另一个散列函数计算地址,直到碰撞不再发生,这种方法不易产生“聚集”,但增加了计算时间(一个地址的产生可能会经过多个散列函数的计算)
Hi=Hn(key), [n=1,2 ...,]
有一个包含一组哈希函数 H1…Hn 的集合。当需要从哈希表中添加或获取元素时,首先使用哈希函数 H1。如果导致碰撞,则尝试使用 H2,以此类推,直到 Hn。所有的哈希函数都与 H1 十分相似,不同的是它们选用的乘法因子。
3.3 拉链法
产生碰撞时,把哈希到同一个槽中的所有元素都放到一个链表中。拉链法采用额外的数据结构来处理碰撞,其将哈希表中每个位置(slot)都映射到了一个链表。
3.4 公共溢出区
建立一个公共溢出区,当发生碰撞时,把碰撞元素放到缓冲区。
四、查询性能
影响产生冲突多少有以下三个因素:
1. 散列函数是否均匀;
2. 处理冲突的方法;
3. 散列表的装填因子。
负载因子(load factor),它用来衡量哈希表的 空/满 程度,一定程度上也可以体现查询的效率,
计算公式为:
负载因子 = 总键值对数 / 箱子个数
负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。因此,一般来说,当负载因子大于某个常数(可能是 1,或者 0.75 等)时,哈希表将自动扩容。