1. 哈希表的基本运算
哈希表的存储结构定义如下:
#define MaxSize 100 //定义最大哈希表长度
#define NULLKEY -1 //定义空关键字值
#define DELKEY -2 //定义被删关键字值
typedef int KeyType; //关键字类型
typedef char * InfoType; //其他数据类型
typedef struct
{
KeyType key; //关键字域
InfoType data; //其他数据域
int count; //探查次数域
} HashData;
typedef HashData HashTable[MaxSize]; //哈希表类型
创建哈希表(除留余数法):
/*
创建哈希表(除留余数法)
HashTable ha : 哈希表
KeyType x[] : 建表的数据
int n : 关键字个数
int m : 哈希表大小
int p : 除留余数法中的p
*/
void CreateHT(HashTable ha,KeyType x[],int n,int m,int p)
{
int i,n1=0;
//初始化哈希表
for (i=0; i<m; i++)
{
ha[i].key=NULLKEY;
ha[i].count=0;
}
for (i=0; i<n; i++)
{ //插入关键字到哈希表
InsertHT(ha, n1, x[i], p ,m);
}
}
插入及建表:
/*
插入及建表
HashTable ha : 哈希表
int n : 哈希表中当前已有关键字个数
KeyType k :要插入的关键字
int p : 除留余数法中的p
*/
void InsertHT(HashTable ha,int &n,KeyType k,int p , int m)
{
int i,adr;
//通过除留余数法计算出当前要插入的关键字的存储地址
adr=k % p;
//然后判断哈希地址是否空闲,是否有哈希冲突
if (ha[adr].key==NULLKEY || ha[adr].key==DELKEY)
{
//没有冲突就可以直接插入
ha[adr].key=k;
ha[adr].count=1;
}
else
{
//哈希地址有冲突,采用线性探查法解决冲突
//i表示探测次数
//如果发生冲突,该循环则会一直探测
i=1;
do
{
adr=(adr+1) % m;
i++;
}
while (ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY);
ha[adr].key=k;
ha[adr].count=i;
}
n++;
}
删除某个关键字:
/*
HashTable ha : 哈希表
int n : 哈希表中已有数据个数
KeyType k : 要删除的数据
int p : 除留余数法中的p
*/
int DeleteHT(HashTable ha,int p,int k,int &n , int m)
{
int adr;
//查找要删除的关键字的存储地址(下标),adr返回-1说明没找到,否则找到了
adr=SearchHT(ha,p,k,m);
if (adr!=-1)
{
//把该关键字标记为删除
ha[adr].key=DELKEY;
n--;
return 1;
}
else
{
return 0;
}
}
查找某个关键字:
/*
查找某个关键字
返回值:返回该关键字的哈希地址
*/
int SearchHT(HashTable ha,int p,KeyType k , int m)
{
int i=0 , adr;
adr=k % p;
//按照线性探测法继续往后找
while (ha[adr].key!=NULLKEY && ha[adr].key!=k)
{
i++;
adr=(adr+1) % m;
}
//查找成功
if (ha[adr].key==k)
return adr;
//查找失败
else
return -1;
}
2. 哈希表示例
将关键字序列{7,8,30,11,18,9,14}散列存储到散列表中,散列表的存储空间是一个下标从0开始的一维数组。,散列函数为:H(key)=(key×3) mod 7,处理冲突采用线性探测再散列法,要求装填(载)因子为0.7。构造的散列表如下:n=7,α = 0.7 = n/m,则m = n/0.7 = 10
计算各关键字存储地址的过程如下:
计算关键字7的哈希地址:H(7)=7×3 mod 7=0,存储到哈希表中下标0的位置,只探测了一次。
计算关键字8的哈希地址:H(8)=8×3 mod 7=3,存储到哈希表中下标3的位置,只探测了一次。
计算关键字30的哈希地址:H(30)=30×3 mod 7=6,存储到哈希表中下标6的位置,只探测了一次。
计算关键字11的哈希地址:H(11)=11×3 mod 7=5,存储到哈希表中下标5的位置,只探测了一次。
在计算关键字为18的哈希地址:H(18)=18×3 mod 7=5,显然关键字18和关键字11的哈希地址起冲突了,那么探测下一个哈希地址,d1=(5+1) mod 10 = 6,发现还是有冲突,继续探测下一个地址d2=(6+1) mod 10 = 7,没有发现冲突,那么把关键字18存储到哈希表中下标为7的位置上。
其他的关键字同理,最后形成的哈希表结构如下图所示:
在等概率情况下:
对于查找成功的平均查找长度来说,我们把哈希表中每个关键字探测次数全部加起来,再除以关键字个数n(n = 7),就得到了查找成功的平均查找长度为:
。
对于查找不成功的平均长度,我们知道由于任一关键字k,H(k)的值只能是0~6之间,不成功的情况,共有7种:
从哈希表结构图来看,当哈希地址为H(k) = 0时,需要比较3次才会出现查找不成功的情况,H(k) = 1时需要比较2次才会出现查找不成功的情况,当H(k) = 2只需要比较1次,当H(k) = 5时就需要比较5次。
于是,我们把哈希表中每个关键字的查找不成功情况的比较次数全部加起来,再除以关键字个数n(n = 7),就得到了查找不成功的平均查找长度: 。