1.定义
一般来说,散列可以浓缩成一句话“将元素通过一个函数转换为整数,使得该整数可以唯一的代表这个元素。”其中把这个转换函数称为散列函数H,也就是说,如果元素在转换前为key,那么转换后就是一个整数H(key)。
那么对于key是整数的情况来说,有哪些常用的散列函数呢?一般来说有以下几种:
①直接定址法:即恒等变换(即H(key)=key)
②线性变换法:即H(key)=a*key+b;
③平方取中法(很少用):取key的平方的中间若干位作为hash值。
④除留余数法:把key除以一个数mod得到的余数作为hash值的方法:H(key)=key%mod
通过这个散列函数,可以把很大的数转换为不超过mod的整数,这样就可以将它作为可行的数组下标。显然当mod是一个素数时,H(key)能尽可能的覆盖[0,mod)范围内的每一个数。
2.冲突及解决办法
但是除留余数法可能会有两个不同的数key1和key2,它们的hash值是相同的。key2不再能使用这个位置了,便冲突。
解决冲突的办法大致有三种:
2.1 线性探查法(Linear Probing)
当得到key的hash值H(key),但是表中下标为H(key)的位置已经被某个其他元素使用了,那么就检查下一个位置H(key)+1是否被占用,如果没有,就使用这个位置;否则就继续检查下一个位置(也就是hash值不断加1)。
如果检查过程中超过了表长,那么就回到表的首位继续循环,直到找到一个可以使用的位置,或者是发现表中所有位置都已被使用。
显然,这个做法容易导致扎堆,即表中利纳许若干个位置都被使用,这代一定程度上会降低效率。
2.2 平方探测法(Quadratic probing)
避免扎堆现象,按下面的顺序:
如果H(key)+k^2 超过了表长TSize,那么就把 H(key)+k^2 对表长TSize取模;
如果H(key)+k^2<0,那么将(H(key)+k^2)%TSize + TSize)%TSize作为结果(等价于将H(Key)-k^2不断加上一个TSize直到出现一个非负数)。
2.3 链地址法(拉链法)
和上面两种方法不同,链地址法不计算新的hash值,而是把所有的H(key)相同的key连接成一条单链表。
这样可以设定一个数组Link,范围是Link[0]~Link[mod],其中Link[h]存放H(Key)=h的一条单链表,于是当多个关键字key的hash都是h时,就可以直接把这些冲突的key直。
接用单链表连接起来,此时就可以遍历这条单链表来寻找所有的H(key)=h的key。
2.4 双散列法
需要使用两个散列函数,当通过第一个散列函数H1(key)得到的地址发生冲突时,则利用第二个散列函数H2(key)计算该关键字的地址增量。它的具体散列函数形式如下:
初始探测位置 。 其中i是冲突的次数, 初始为0。在双散列法中,最多经过m-1次探测就会遍历表中的所有位置,回到位置。
3.散列查找及性能分析
这里不多赘述了,比较简单,贴上图