散列查找
1.散列表
散列是在记录的存储位置和它的关键字之间建立一个确定的对应关系F,使得每个关键字key对应一个存储位置F(key)。
这里把这种对应关系F称为散列函数,又称为哈希(Hash)函数。
采用散列将记录存在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表。那么,关键字对应的记录存储位置称为散列地址。
2.散列函数的构造方法
(1)直接定值法
取某个关键字的某个线性函数值为散列地址,即散列函数为
F(key)=a*key+b(a,b均为常数)
(2)除留余数法(较为常用)
散列函数为:F(key)= key mod p;
把我们的关键字 通过求余运算把一个大的数变成一个小的数,这个小的数就是我们散列表当中的地址。
一般的会把 p= 素数;
(3)数字分析法
数字分析法是希望分析出对象的关键字在每一位上的表现,我们把那些能够随机变化的位取出来作为散列地址,达到地址均匀的目的。
(4)折叠法
把很长数字的关键词拆成若干个相同长度的部分,然后将它叠加。
(5)平方取中法
把一个大数取一个平方,然后取这个数字的中间几位。
3.字符关键字的散列函数构造
(1)ASCLL码加和法
一个字符串由若干个字符构成,我要把字符串这一个对象映射成一个整数,把每一个字符(它是一个ASCLL码的编码),然后把每一个数字相加,再求余。
F(key)=(∑key[i])mod p;
但这个方法也会有冲突,而且很容易产生聚集。
(2)在第一个方法上的改进——前3个字符移位法
F(key)=(key[0]*27^2 + key[1]*27 + key[2])mod p; (因为有26个字母,可能当中会用空格所以用27)
但此方法仍然会有冲突和空间的浪费。
(3)在第二个方法上的改进——取所有字符移位法(hash算法)
F(key)=(∑key[n-i-1]*32*i)mod p; (0<= i <=n-1)
32代表32进制
哈希算法详细介绍:
https://blog.csdn.net/huyidai/article/details/104146092
4.关于冲突的处理方法
(1)开放地址法:一旦产生了冲突(该地址已有其他元素),就按某一规则去寻找另一空地址。
若发生了第i次冲突,试探的下一地址将增加di。
F(key)=(F(key)+di)mod p;
线性探测:di=i;在原来的基础上产生一个增量序列(1,2,……p-1)循环试探下一个地址。
平方探测:di=土 i^2; 在原来的基础上产生一个增量序列(1^2,-1^2, 2^2,- 2^2……q^2,-q^2)循环试探下一个地址。
q<=(p/2);
双散列:di= i*f(key); di为 i*f(key),f(key)为另一个散列函数;
在原来的基础上产生一个增量序列(f(key),2*f(key) ,3*f(key),……)循环试探下一个地址。
再散列:当散列元素太多(装填因子太大),查找速率会下降;因此散列表扩大时原有元素需要重新计算放置到新表中。
一般的实用最大装填因子 取【0.5 <=a<= 0.85】
(2)分离链接法
将相应位置上冲突的所有关键词储存在同一单链表中。
5.散列表的性能分析
平均查找长度(ASL)用来度量散列表的查找效率:成功(要查找的元素在散列表中),不成功;
关键字的比较次数在于产生冲突的多少:有三个因素
(1)散列函数是否均匀;
(2)处理冲突的方法;
(3)散列表的装填因子a。
线性探测:成功:1/2*(1+1/(1-a));
不成功:1/2*(1+1/(1-a)*2);
平方探测和双散列:成功:-1/a*ln(1-a);
不成功:1/(1-a);
分离链接法:
成功:1+a/2;
不成功:a+e^-a;
特点:
(1)散列(哈希)查找选择合适的F(key),查找效率期望值是O(1),它几乎与关键字的空间大小无关,也适合于关键字直接比较计算量大的问题。
(2)它是以较小的装填因子a为前提,因此,散列查找是一个以空间换时间的策略。
(3)散列方法的储存对关键字是随机的,不便于顺序查找,也不适合于范围查找,或最大值最小值查找。