1.采用散列表技术需要考虑的两个主要问题
(1)散列函数的设计。如何设计一个简单、均匀 、存储利用率高的散列函数。
(2)冲突的处理。如何采取合适的处理冲突方法来解决冲突。
2.散列函数的设计
(1)基本原则:
a.计算简单。散列函数不应该有很大的计算量,否则会降低查找效率。
b.函数值即散列地址分布均匀。函数值要尽量均匀散步在地址空间,这样才能保证存储空间的有效利用,并减少冲突。
(2)集中常见的散列函数:
a.直接定址法。
直接定址法 f(key) = a * key + b(a、b为常数)。
取关键字的某个线性函数值为散列地址。(这样的散列函数简单、均匀、不会产生冲突;需要事先知道关键字的分布情况,适合查找表较小且连续的情况,较少使用)
b.除留余数法
此方法为最常用的构造散列函数的方法。对于散列表长为m的散列函数公式为:f(key) = key mod p(p≤m)
这种方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。散列表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。
c.数字分析法
抽取关键字的一部分来计算散列函数位置的方法。适合处理关键字位数比较大的情况,(如果事先知道关键字的分布且关键字的若干位分布比较均匀,就可以考虑用这个方法。)
d.平方取中法
计算关键字的平方,再取中间几位做为关键字。eg:1234,平方1522756,取中间3为227为关键字。(平方取中法比较适合不知道关键字的分布,而位数又不是很大的情况。)
e.折叠法
将关键字从左到右分割成位数相等的几部分,然后将这几部分叠加求和,并按散列表表长,取后几位作为散列表地址。(折叠法事先不需要知道关键字的分布,适合关键字位数较多的情况。)
3.处理冲突的方法
(1)开放定址法
一旦发生了冲突,就去寻找下一个空的散列地址,知道散列表足够大,空的散列表地址总能找到,并将记录存入。这种解决冲突的开放定址发称为线性探测法。
fi(key) = (f(key) + di) MOD m (di = 1,2,3,…,m-1)
在发生冲突进行重新定址的过程中会导致后面不是同义词的关键字( f(keyi) ≠ f(key2) )争夺一个地址的情况,这种现象为堆积。
线性探测都是发生冲突后加上一个di然后取余数来寻找下一个空间地址,如果发生冲突的位置之前就有一个空位置,要找到这个空位置要循环效率就很低
fi(key) = (f(key) + di) MOD m (di = 12,-12,22,…,q2,-q^2,q≤m/2)
增加平方运算的目的是为了不让关键字都聚集在某一块区域,这种方法叫做二次探测法。
在冲突时,对于位移量di采用随机函数计算得到。
fi(key) = (f(key) + di) MOD m (di 是一个伪随机数列) 。 伪随机,只要随机种子相同,每次得到的数列都会是相同的。这就是随机探测法 。
(2)拉链法(链地址法)
为了解决线性探测实现哈希表时出现哈希冲突后导致数据堆聚的现象,我们采取了另一种结构来处理哈希冲突,哈希桶(拉链法),拉链法解决冲突的做法是将所有通过哈希函数计算出来的哈希地址相同的结点存放在一个单链表之中。
实现原理就是将哈希表定义为一个由N个头指针组成的指针数组,经过哈希函数计算得到的哈希地址相同的数据全部连在对于的头指针下面。它继承了数组的易于查找和链表便于删除插入的特点。哈希桶的实现(拉链法)
参考
https://blog.csdn.net/MBuger/article/details/62418754
https://www.cnblogs.com/meihao1203/p/9277399.html