数据结构-哈希表(散列表)-查找-除留余数法-开放地址法
//代码附有详细注释
哈希表的动态生成和查找,
哈希函数为除留余数法,
处理冲突的方法为开放地址法。
常量,KC 关键码的个数
//常量
#define KC 14 //关键码的个数
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
#define NULLKEY 0
#define EQ(a, b) ((a)-(b))
#define LT(a, b) ((a)<(b))
#define LQ(a, b) ((a)<=(b))
哈希表容量递增表,一个合适的素数序列,m是哈希表表长,全局变量
//哈希表容量递增表,一个合适的素数序列,m是哈希表表长,全局变量
int hashsize[]={13,19,29,37};
int m=0;
给元素和关键字起一个别名,如果需要更换元素和关键字类型只需要更改一处地方,并且写代码时ElemType比int更容易理解。
//元素类型,关键字类型
typedef int ElemType;
typedef int KeyType;
typedef int Status;
开放定址哈希表的存储结构
//定义开放定址哈希表的存储结构
struct HashTable
{
ElemType *elem; //数据元素存储基址,动态分配数组
int count; //当前数据元素个数
int sizeindex; //hashsize[sizeindex]为当前容量
};
哈希表的初始化
//哈希表的初始化
Status lnitHashTable(HashTable *H)
{
int i;
m=hashsize[0];//hashsize[0]=13
(*H).count=0; //当前元素个数为0
(*H).sizeindex=m; //初始存储容量
(*H).elem=(ElemType*) malloc(m*sizeof(ElemType));//给哈希表申请空间地址
if(!(*H).elem)//存储分配失败
{
printf("哈希表建立失败\n");
exit(UNSUCCESS);
}
for(i=0; i<m; i++)
{
(*H).elem[i]=NULLKEY; //未填记录的标志
}
return SUCCESS;
}
哈希表的销毁
申请动态空间后,注意在使用结束后释放动态空间。
//哈希表的销毁
void DestroyHashTable(HashTable *H)//初始条件:哈希表H存在
{
free((*H).elem);
(*H).elem=NULL;
(*H).count=0;
(*H).sizeindex=0;
}
哈希函数:除留余数法
冲突解决办法:开放地址法
//哈希函数
unsigned int Hash(KeyType K)
{
printf("\n关键码: %d 哈希函数 %d \n", K, K%m);
return K%m;
}
//冲突解决方法(线性探测再散列)
void collision(int *p, int d)
{
//开放定址法处理冲突
*p=(*p+d)%m;
printf("处理冲突后哈希地址 %d ",*p);
}
在开放定址哈希表H中查找关键码为K的元素
//查找(若该数据元素不在表中,则插入)
Status SearchHash(HashTable H, KeyType K, int *p, int *c)
{
//在开放定址哈希表H中查找关键码为K的元素,若查找成功,以P指示待查数据元素在表中位置,并返回SUCCESS;
//否则,以P指示插入位置,并返回 UNSUCCESS
//c用以计冲突次数,其初值置零,供建表插入时参考。
*p=Hash(K);//通过哈希函数求得哈希地址
printf("哈希地址: %d ",*p);
printf("此位置是否已有关键码: %d \n",H.elem[*p] != NULLKEY);
while( H.elem[*p] != NULLKEY && EQ(K, H.elem[*p]) )
//位置中填有记录,并且关键字不相等
{
(*c)++;
if(*c<m)
collision(p,*c); //求得下一探查地址P
else
break;
printf("处理冲突后的位置是否已有关键码: %d \n",H.elem[*p] != NULLKEY);
}
if (!EQ(K, H.elem[*p]))
{
return SUCCESS; //查找成功,P返回待查数据元素位置
}
else
return UNSUCCESS;
}
哈希表的重建
如果当前哈希表容量不够存储关键码(冲突次数过大),则需要重建哈希表
//哈希表的重建
Status lnsertHash(HashTable *H, ElemType);//lnsertHash函数的声明
void RecreateHashTable(HashTable *H, ElemType e)//重建哈希表
{
int i;
int count=(*H).count;
ElemType *elem;
ElemType *p=(ElemType*) malloc(count*sizeof(ElemType));
printf("原先的的哈希表:\n");
for(i=0; i<m; i++)//保存原有的数据到p中
{
if(((*H).elem[i]) != NULLKEY) //该单元有数据
{
p[i]=(*H).elem[i];
printf("(%d,%d) ",i,p[i]);
}
else
{
p[i]=NULLKEY;
printf("(%d,%d) ",i,p[i]);
}
}
printf("\n");
(*H).count=0;
(*H).sizeindex++;//增大存储容量
m=(*H).sizeindex;
printf("增加哈希表的存储容量到%d \n新建哈希表:",m);
elem=(ElemType*) realloc( (*H).elem, m*sizeof(ElemType));
if(!elem) exit(0); //存储分配失败
(*H).elem=elem;
for(i=0; i<m; i++) (*H).elem[i]=NULLKEY; //未填记录的标志(初始化)
for(i=0; i<m-1; i++)
{
lnsertHash(H,p[i]);//将原有的数据按照新的表长插入到重建的哈希表中
}
lnsertHash(H,e);//最后一个数据
}
在哈希表中插入数据元素
//在哈希表中插入数据元素
Status lnsertHash(HashTable *H, ElemType e)
{
//查找不成功时插入数据元素e到开放定址哈希表H中,并返回0K;
//若冲突次数过大,则重建哈希表
int c,p;
c=0;
if(SearchHash(*H,e,&p,&c)) //SearchHash函数返回1则说明表中已有与e有相同关键字的元素
{
printf("表中已有相同关键字的元素:%d\n", e);
return DUPLICATE;
}
else
if( c < m) //最大冲突次数
{
printf("冲突次数%d未达到上限\n", c );
(*H).elem[p]=e;//插入e
(*H).count++;
return SUCCESS;
}
else
{
printf("冲突次数达到上限时关键码为%d \n",e);
RecreateHashTable(H,e); //重建哈希表
}
return UNSUCCESS;
}
哈希表的遍历
//哈希表的遍历
void TraverseHash(HashTable H)
{
//按哈希地址的顺序遍历哈希表
int i;
printf("哈希地址0~%d\n", m-1);
for(i=0; i<m; i++)
if(H.elem[i]!=NULLKEY) //有数据
printf("(%d, %d) ",i,H.elem[i]);
printf("\n");
return;
}
主函数用于测试,数组数量在hashsize[0]之前无重建,hashsize[0]之后有重建。是否重建还与数组元素有关。
void main()
{
KeyType key[]={19,01,23,14,55,20,84,27,68,11,10,77,63,45,24};//可自定义,但效果不一定很好。
HashTable H;
ElemType e;
lnitHashTable(&H); //构建了一个空的哈希表
for(int i=0; i<KC; i++)
{
e=key[i];
lnsertHash(&H, e);
}
TraverseHash(H);
DestroyHashTable(&H);//哈希表的销毁
}
最后,如果觉得这篇博客能帮助到你,请点个小小的赞支持一下我(收藏也行呀,嘻嘻),这是我继续更新的动力呀,关注我可以得到第一时间的更新,更加快人一步哦。
如果有问题请在评论区留言,一起交流进步。
附完整代码:
#include "stdio.h"
#include "stdlib.h"
//常量
#define KC 14 //关键码的个数
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
#define NULLKEY 0
#define EQ(a, b) ((a)-(b))
#define LT(a, b) ((a)<(b))
#define LQ(a, b) ((a)<=(b))
//哈希表容量递增表,一个合适的素数序列,m是哈希表表长,全局变量
int hashsize[]={13,19,29,37};
int m=0;
//元素类型,关键字类型
typedef int ElemType;
typedef int KeyType;
typedef int Status;
//定义开放定址哈希表的存储结构
struct HashTable
{
ElemType *elem; //数据元素存储基址,动态分配数组
int count; //当前数据元素个数
int sizeindex; //hashsize[sizeindex]为当前容量
};
//哈希表的初始化
Status lnitHashTable(HashTable *H)
{
int i;
m=hashsize[0];//hashsize[0]=13
(*H).count=0; //当前元素个数为0
(*H).sizeindex=m; //初始存储容量
(*H).elem=(ElemType*) malloc(m*sizeof(ElemType));//给哈希表申请空间地址
if(!(*H).elem)//存储分配失败
{
printf("哈希表建立失败\n");
exit(UNSUCCESS);
}
for(i=0; i<m; i++)
{
(*H).elem[i]=NULLKEY; //未填记录的标志
}
return SUCCESS;
}
//哈希表的销毁
void DestroyHashTable(HashTable *H)//初始条件:哈希表H存在
{
free((*H).elem);
(*H).elem=NULL;
(*H).count=0;
(*H).sizeindex=0;
}
//哈希函数
unsigned int Hash(KeyType K)
{
printf("\n关键码: %d 哈希函数 %d \n", K, K%m);
return K%m;
}
//冲突解决方法(线性探测再散列)
void collision(int *p, int d)
{
//开放定址法处理冲突
*p=(*p+d)%m;
printf("处理冲突后哈希地址 %d ",*p);
}
//查找(若该数据元素不在表中,则插入)
Status SearchHash(HashTable H, KeyType K, int *p, int *c)
{
//在开放定址哈希表H中查找关键码为K的元素,若查找成功,以P指示待查数据元素在表中位置,并返回SUCCESS;
//否则,以P指示插入位置,并返回 UNSUCCESS
//c用以计冲突次数,其初值置零,供建表插入时参考。
*p=Hash(K);//通过哈希函数求得哈希地址
printf("哈希地址: %d ",*p);
printf("此位置是否已有关键码: %d \n",H.elem[*p] != NULLKEY);
while( H.elem[*p] != NULLKEY && EQ(K, H.elem[*p]) )
//位置中填有记录,并且关键字不相等
{
(*c)++;
if(*c<m)
collision(p,*c); //求得下一探查地址P
else
break;
printf("处理冲突后的位置是否已有关键码: %d \n",H.elem[*p] != NULLKEY);
}
if (!EQ(K, H.elem[*p]))
{
return SUCCESS; //查找成功,P返回待查数据元素位置
}
else
return UNSUCCESS;
//查找不成功Helem[*p]==NULLKEY,P返回的是插入位置
}
//哈希表的重建
Status lnsertHash(HashTable *H, ElemType);//lnsertHash函数的声明
void RecreateHashTable(HashTable *H, ElemType e)//重建哈希表
{
int i;
int count=(*H).count;
ElemType *elem;
ElemType *p=(ElemType*) malloc(count*sizeof(ElemType));
printf("原先的的哈希表:\n");
for(i=0; i<m; i++)//保存原有的数据到p中
{
if(((*H).elem[i]) != NULLKEY) //该单元有数据
{
p[i]=(*H).elem[i];
printf("(%d,%d) ",i,p[i]);
}
else
{
p[i]=NULLKEY;
printf("(%d,%d) ",i,p[i]);
}
}
printf("\n");
(*H).count=0;
(*H).sizeindex++;//增大存储容量
m=(*H).sizeindex;
printf("增加哈希表的存储容量到%d \n新建哈希表:",m);
elem=(ElemType*) realloc( (*H).elem, m*sizeof(ElemType));
if(!elem) exit(0); //存储分配失败
(*H).elem=elem;
for(i=0; i<m; i++) (*H).elem[i]=NULLKEY; //未填记录的标志(初始化)
for(i=0; i<m-1; i++)
{
lnsertHash(H,p[i]);//将原有的数据按照新的表长插入到重建的哈希表中
}
lnsertHash(H,e);//最后一个数据
}
//在哈希表中插入数据元素
Status lnsertHash(HashTable *H, ElemType e)
{
//查找不成功时插入数据元素e到开放定址哈希表H中,并返回0K;
//若冲突次数过大,则重建哈希表
int c,p;
c=0;
if(SearchHash(*H,e,&p,&c)) //表中已有与e有相同关键字的元素
{
printf("表中已有相同关键字的元素:%d\n", e);
return DUPLICATE;
}
else
if( c < 12)
{
printf("冲突次数%d未达到上限\n", c );
(*H).elem[p]=e;//插入e
(*H).count++;
return SUCCESS;
}
else
{
printf("此时关键码为%d \n",e);
RecreateHashTable(H,e); //重建哈希表
}
return UNSUCCESS;
}
//哈希表的遍历
void TraverseHash(HashTable H)
{
//按哈希地址的顺序遍历哈希表
int i;
printf("哈希地址0~%d\n", m-1);
for(i=0; i<m; i++)
if(H.elem[i]!=NULLKEY) //有数据
{
printf("(%d, %d) ",i,H.elem[i]);
if(i%5==0) printf("\n");
}
printf("\n");
return;
}
void main()
{
KeyType key[]={19,01,23,14,55,20,84,27,68,11,10,77,63,45,24};
HashTable H;
ElemType e;
lnitHashTable(&H); //构建了一个空的哈希表
for(int i=0; i<KC; i++)
{
e=key[i];
lnsertHash(&H, e);
}
TraverseHash(H);
DestroyHashTable(&H);//哈希表的销毁
system("pause");
}