1.哈希表的概念:
哈希是一种重要的存储方法,也是一种重要的查找方法。
基本思想:以关键字K为自变量,通过一个确定的函数f,计算出对应的函数值f (k),把这个值解释为关键字等于K的结点的存储地址。查找时,再根据要查找的关键字用同样的函数计算地址,然后到相应的存储单元取出要查找的结点。按这个思想建立的表,称为哈希表,称函数f 为哈希函数,称f (k)的值为哈希地址。
2. 哈希函数的构造方法:
2.1 直接定址法:
取关键字或关键字的某个线性函数值为哈希地址。即: H(key) = key 或 H(key) = a*key+b 直接定址法由于关键字与存储地址存在一一对应关系,因此,不会 发生冲突现象。
2.2 求模法:
选择一个适当的正整数P(P≤表长),用P 去除关键字,取所得余数作为哈希地址。即:H(key) = key % P (P ≤ 表长) 求模法的关键是选取适当的P,一般选P为小于或等于哈希表的长 度m的某个素数为好。
2.3 平方取中法:
取关键字平方后的中间几位为哈希地址。由于一个数的平方的中间几位与这个数的每一位都有关,因而,平方取中法产生冲突的机会相对较小。平方取中法中所取的位数由表长决定。
2.4 折叠法:
把一个关键码分成位数相同的几段(最后一段的位数可以, 不同),段的长度取决于哈希表的地址位数,然后将各段的 叠加和(舍去进位)作为哈希地址。折叠法又分为移位叠加和边界叠加两种。其中,移位叠加是将 各段的最低位对齐,然后相加;而边界叠加则是两个相邻的段沿边界来回折叠,然后对齐相加。
2.5 数字分析法:
假设关键字是以r为基的数,并且哈希表中可能 出现的关键字都是事先知道的,则可取关键字中的若干位组成哈希地址。
3. 处理冲突的方法:
3.1 开放定址法:
基本做法:当冲突发生时,使用某种方法在哈希表中形成一探查序列,然后沿着此探查序列逐个单元地查找,直到碰到一个开放的地址(即该地址单元为空)为止。在哈希表中形成一探查序列时,可有三种不同的方法:
⑴ 线性探测法:
基本思想:将散列看成是一个环形表,探测序列是(假设表长为m):
H(k),H(k)+1,H(k)+2,…,m-1,0,1,…,H(k)-1
用线性探法解决冲突时,求下一个开放地址的公式为:
Hi = (H(k)+i) MOD m
⑵ 二次探测法:
二次探测法的探测序列依次是12,-12,22,-22,…等,当发生冲突时,求下一个开放地址的公式为:
H2i-1 = (H(k)+i2) MOD m
H2i = (H(k)-i2) MOD m (1=< i <= (m-1)/2 )
优点:减少了堆集发生的可能性。
缺点:不容易探测到整个哈希表空间。
⑶ 伪随机探测法:
采用随机探查法解决冲突时,求下一个开放地址的公式为:
Hi = (H(k)+Ri) MOD m
其中:R1,R2,…,Rm-1是1,2,…,m-1的一个随机排列。如何得随机排列,涉及到随机数的产生问题。
3.2 再哈希法:
基本做法:当冲突发生时,使用另一个哈希函数计算得到一个新的哈希地址,直到冲突不再发生时为止,即Hi = RHi(key) ,i = 1,2,…,k,其中,RHi均是不同的哈希函数。
这种方法的优点是不易产生"堆集",但缺点是增加了计算时间。
3.3 链地址法:
基本做法:将所有关键字为同义词的结点链接在同一个单链表中。若选定的哈希函数所产生的哈希地址为0~m-1,则可将哈希表定义成一个由m个链表头指针组成的指针数组。
这种方法的优点是:① 不产生"堆集"。
② 由于结点空间是动态申请的,故更适合于造表前无法确定表长的情况。
③ 从表中删除结点容易。
3.4 公共溢出区法法:
基本做法:假设哈希函数的值域为[0..m-1],则设向量HashTable[0..m-1]为基本表,每个分量存放一个记录,另设立向量OverTable[0..v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都被填入溢出表中。
采用求模法和开放定址法的实例如下:
#include<iostream>
using namespace std;
int b[11];
int H(int k)
{
return k % 11;
}
void HashBuild(int a[],int n)
{
int j,i;
for (i = 0; i < n; i++)
{
j = H(a[i]);
while (b[j])
{
j = H(j + 1);
}
b[j] = a[i];
}
}
int Search(int k)
{
int i = H(k);
while (b[i])
{
if (b[i] == k)
return 1;
i = H(i + 1);
}
return 0;
}
int main()
{
int a[] = {47,7,29,11,16,92,22,8,3};
HashBuild(a,9);
if (Search(7))
printf("Find it!\n\n");
else
printf("Not Find!\n\n");
return 0;
}