数据结构-哈希表(散列表)-查找方法-开放地址法

数据结构-哈希表(散列表)-查找-除留余数法-开放地址法
//代码附有详细注释
哈希表的动态生成和查找,
哈希函数为除留余数法,
处理冲突的方法为开放地址法。

常量,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");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

“翎羽”

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值