</pre><pre name="code" class="csharp">#include <stdio.h>
#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]; //哈希表类型
void InsertHT(HashTable ha,int &n,KeyType k,int p) //将关键字k插入到哈希表中
{
int i,adr;
adr=k % p;
if (ha[adr].key==NULLKEY || ha[adr].key==DELKEY) //x[j]可以直接放在哈希表中
{
ha[adr].key=k;
ha[adr].count=1;
}
else //发生冲突时采用线性探查法解决冲突
{
i=1; //i记录x[j]发生冲突的次数
do
{
adr=(adr+1) % p;
i++;
}
while (ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY);
ha[adr].key=k;
ha[adr].count=i;
}
n++;
}
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);
}
int SearchHT(HashTable ha,int p,KeyType k) //在哈希表中查找关键字k
{
int i=0,adr;
adr=k % p;
while (ha[adr].key!=NULLKEY && ha[adr].key!=k)
{
i++; //采用线性探查法找下一个地址
adr=(adr+1) % p;
}
if (ha[adr].key==k) //查找成功
return adr;
else //查找失败
return -1;
}
int DeleteHT(HashTable ha,int p,int k,int &n) //删除哈希表中关键字k
{
int adr;
adr=SearchHT(ha,p,k);
if (adr!=-1) //在哈希表中找到该关键字
{
ha[adr].key=DELKEY;
n--; //哈希表长度减1
return 1;
}
else //在哈希表中未找到该关键字
return 0;
}
void DispHT(HashTable ha,int n,int m) //输出哈希表
{
float avg=0;
int i;
printf(" 哈希表地址:\t");
for (i=0; i<m; i++)
printf(" %3d",i);
printf(" \n");
printf(" 哈希表关键字:\t");
for (i=0; i<m; i++)
if (ha[i].key==NULLKEY || ha[i].key==DELKEY)
printf(" "); //输出3个空格
else
printf(" %3d",ha[i].key);
printf(" \n");
printf(" 搜索次数:\t");
for (i=0; i<m; i++)
if (ha[i].key==NULLKEY || ha[i].key==DELKEY)
printf(" "); //输出3个空格
else
printf(" %3d",ha[i].count);
printf(" \n");
for (i=0; i<m; i++)
if (ha[i].key!=NULLKEY && ha[i].key!=DELKEY)
avg=avg+ha[i].count;
avg=avg/n;
printf(" 平均搜索长度ASL(%d)=%g\n",n,avg);
}
int main()
{
int x[]= {16,74,60,43,54,90,46,31,29,88,77};
int n=11,m=13,p=13,i,k=29;
HashTable ha;
CreateHT(ha,x,n,m,p);
printf("\n");
DispHT(ha,n,m);
i=SearchHT(ha,p,k);
if (i!=-1)
printf(" ha[%d].key=%d\n",i,k);
else
printf(" 未找到%d\n",k);
k=77;
printf(" 删除关键字%d\n",k);
DeleteHT(ha,p,k,n);
DispHT(ha,n,m);
i=SearchHT(ha,p,k);
if (i!=-1)
printf(" ha[%d].key=%d\n",i,k);
else
printf(" 未找到%d\n",k);
printf(" 插入关键字%d\n",k);
InsertHT(ha,n,k,p);
DispHT(ha,n,m);
printf("\n");
return 0;
}
运行结果:
知识点总结:
哈希表的概念
哈希表(Hash Table)也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构。它通过把关键码值映射到哈希表中的一个位置来访问记录,以加快查找的速度。这个映射函数就做散列函数,存放记录的数组叫做散列表。
散列存储的基本思路
以数据中每个元素的关键字K为自变量,通过散列函数H(k)计算出函数值,以该函数值作为一块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中。
哈希表查找的时间复杂度
哈希表存储的是键值对,其查找的时间复杂度与元素数量多少无关,哈希表在查找元素时是通过计算哈希码值来定位元素的位置从而直接访问元素的,因此,哈希表查找的时间复杂度为O(1)。
哈希函数的构造方法
哈希表处理冲突主要有开房寻址法、再散列法、链地址法(拉链法)和建立一个公共溢出区四种方法。
1. 直接寻址法
取关键字或者关键字的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这种散列函数也叫做自身函数.如果H(Key)的哈希地址上已经有值了,那么就往下一个位置找,知道找到H(Key)的位置没有值了就把元素放进去.
2. 数字分析法
分析一组数据,比如一组员工的出生年月,这时我们发现出生年月的前几位数字一般都相同,因此,出现冲突的概率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果利用后面的几位数字来构造散列地址,则冲突的几率则会明显降低.因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址.
3. 平方取中法
取关键字平方后的中间几位作为散列地址.
4. 折叠法
折叠法即将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(注意:叠加和时去除进位)作为散列地址.数位叠加可以有移位叠加和间界叠加两种方法.移位叠加是将分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加.
5. 随机数法
选择一个随机数,去关键字的随机值作为散列地址,通常用于关键字长度不同的场合.
6. 除留余数法
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址.即H(Key)=Key MOD p,p<=m.不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选得不好,则很容易产生冲突。