刚刚开始研究数据结构,看的头大,这里简单总结下这两天学习Hash表的结果!各位看官请轻拍!
对于查找来说,一般来说使用的是关键字查找,关键字越特别,查找的结果越准确。那么我们在设计一个查找表时,关键字和查找方式就是最重要的两个部分。
哈希表,又称为散列表,按照数据表中每一个记录的关键字k对其进行存储,在理想情况下,通过哈希函数H在关键字与地址之间建立起一一对应的关系,那么查找时只需要进行一次计算即可。但是当出现不同的关键字对应同一个存储地址,即k1≠k2,但是H(k1)=H(k2),那么这种情况就称为冲突。把这种具有不同关键字值而具有相同哈希地址的对象称为“同义词”。
在构造哈希表时,需要解决的两个问题就是:
1.如何设计哈希函数,使得冲突尽可能少
2.发生冲突后如何解决
Hash函数的构造方法:
1.直接定址法
是以关键字本身k或者是关键字加上某一个数值常量c作为哈希地址。
H(k)=k+c
这种哈希函数计算简单,并且不可能有冲突发生,当关键字的分布基本连续时,可以用直接定址法。若关键字的分布不连续,直接定址法将造成内存单元的大量浪费。
2.除留余数法
取关键字k除以哈希表长度m,所得余数作为哈希地址。
H(k)=k%m
这是一种比较简单,也比较常见的构造方法。这种方法的关键是选好哈希表的长度m,使得数据集合中的每一个关键字通过函数转化后映射到哈希表上的任意地址的概率相等。在m取素数时,发生冲突的可能性小。
3.平方取中法
取关键字的平方后的中间几位作为哈希地址。
4.折叠法
这种方法适合在关键字位数较多,且地址区间较小的情况下。将关键字分隔成数位相同的几部分,然后将这几部分的叠加和作为哈希地址,若超出范围可再取模。
5.数值分析法
如果事先知道所有可能的关键字的取值,那么就可以对通过对关键字的分析,发现其变化规律,从而构造出相应的哈希函数,得到哈希地址。
6.随机数法:
对长度不等的关键字构造哈希函数。
设定哈希函数为:H(key) = Random(key)其中,Random 为伪随机函数。
Hash表处理冲突的方法:
处理冲突就是为该关键字找到一个另一个“空”的哈希地址。
1.开放定址法当发生冲突时,形成一个地址序列,沿着这个序列逐个探测,直到找到一个“空”的开放地址,将发生冲突的关键字值存放到该地址中去。
如 Hi=(H(k)+d(i))%m i=1,2,...,k (k<m-1)
其中H(k)为哈希函数,m为哈希表的长度,d(i)为增量函数,d(i)=d1,d2,...,dn-1。
根据增量函数的取法不同,可以得到不同的开放定址处理冲突探测方法。
(1)线性探查法
从发生冲突的地址(设为d)开始,依次探查:d+1,d+2,...d+m-1,当探查到表位m-1时,又从0开始探查,直到找到一个空闲的位置来存放发生冲突的关键字。
若整个地址都找遍,仍没有空地址,则产生溢出。
线性探测法的数序递推公式为:
d0=H(k)
...
d(i)=(d(i-1)+1)%m,直到找到一个空地址。
利用线性探查法处理冲突容易造成关键字的“堆积”问题。这是因为当连续n个单元被占用后,再散列到这些单元上的关键字和直接散列到后面一个空闲单元上的关键字都要占用这个空闲单元,致使该空闲单元很容易被占用,从而发生非同义冲突,造成平均查找长度的增加。
为了克服堆积问题,则可用下面这个方法
(2)平方探查法
假设发生冲突的地址为d,则平方探查法的探查序列为:d+1^2,d+2^2,...,直到找到空闲地址为止。
其数学公式为:
d0=H(k)
...
d(i)=(d0+i^2)%m
平方探查法可以避免出现堆积问题,但缺点是无法探查到哈希表上的所有单元,但至少能探查到一半单元。
若解决冲突时,探查到一半单元仍然找不到一个空闲单元,则表明此哈希表太满,需要重现建立哈希表。
2.链地址法
把所有关键字为同义词的记录存储在一个线性链表中,这个链表称为同义词链表,并将这些链表的表头放在数组中(下标从0到m-1)。
链地址法虽然要多费一些存储空间,但可以彻底解决堆积问题,大大提高查找效率。