对哈希函数及算法的理解

哈希函数的本质就是取关键字的一些特征来确定存储的位置。
1.直接定址法
假如将arr[4]={12,5,18,10,7,9};存到arr1[MAXSIZE]里面可以按如下存

125181079
arr1[12]arr1[5]arr1[18]arr1[10]arr1[7]arr1[9]

其他的位置存0
缺点:可知这种存法很浪费空间,存了好多没必要的0
优点:对于存储数值比较小且不重合的数组来说,比较方便。比如arr[4]={1,2,3,4};刚合适
2.数字分析法
在这里插入图片描述
优点:感觉这样存储的优点不太明显,你可能会想这样换不如直接存储的方便,其实这样存储的优点在于哈希的搜索比较方便。
3.除留余数法
任何形式的存储都可以用除留余数法来获得哈希地址,因为在计算机内部都是以数字的形式进行存储的,字母和字符都可以通过ASCII表转化为相应的数字,然后确定除数就可以获得地址。
比如名字Sofency这个字符串,我可以获取S来存储,S对应的数字是83%13=5
那么我就可以用地址5存放名字Sofency。
优点:使用范围广
缺点:选择的除数要合适,避免冲突的地址太多。

  • 解决冲突的方法
    线性探测再散列:在除留余数法的基础上再进行计算
    比如

    key=83%13,key=5;而此时5的位置有数据在占着
    key=(key+1)%13当然13的数字可以调节,但是一般不调节

仔细研究了sanqima大佬的代码后对其算法的理解更进一步
对大佬代码的分析如下

#include<stdio.h>
#include <stdlib.h>
#define MAXSIZE 100//定义最大哈希表长度
#define NULLKEY -1   //定义空关键字值
#define DELKEY -2   //定义被删关键字值
typedef int KeyType; //关键字类型
typedef char * InfoType;//其它数据类型  ///解释下为什么用char*  是为了如果你写的是字符类型的数据的话便于存储
typedef struct
{
	KeyType key;//关键字域
	InfoType data;//其它数据域//本代码中没有其他类型的数据,这也是作者为我们小白练手提供了一个机会
	int count;//探查次数域
}HashTable[MAXSIZE];//哈希表的类型

void InsertHT(HashTable ha, int &n, KeyType k, int p)//p暂且定义为hash除数
{
	int i, adr;
	adr = k%p;
	if (ha[adr].key == NULLKEY || ha[adr].key == DELKEY)
	{
		ha[adr].key = k;//如果k是字符串的话,存储可以用字符串赋值即strcpy(ha[adr].data,k);也就是上面结构体的其他类型数据
		ha[adr].count = 1;
	}
	else
	{
		i = 1;
		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)//创建哈希表,n代表哈希表关键字的个数
{
	int i, n1 = 0;
	for (i = 0; i < m; i++)//哈希表置初值m相当于一个瓶子的最大容量    n表示现在装的量  p表示哈希因子
	{
		ha[i].key = NULLKEY;
		ha[i].count = 0;
	}
	for (i = 0; i < n; i++)
	{
		InsertHT(ha, n1, x[i], p);
	}
}
//在哈希表中查找关键字k
int SearchHT(HashTable ha, int p, KeyType k)
{
	int i = 0, adr;
	adr = k%p;
	while (ha[adr].key != NULLKEY&&ha[adr].key != k)//如果是字符串的话用strcmp函数进行比较
	{
		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)//n?
{
	int adr;
	adr = SearchHT(ha, p, k);
	if (adr != -1)//查找成功
	{
		ha[adr].key = DELKEY;//赋值被删除关键字
		n--;
		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("   ");
		}
		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)
		{
			avg = avg + ha[i].count;
		}
	}
	avg = avg / n;
	printf("平均搜索长度ASL(%d)=%.3f\n", n, avg);
}
//查找成功是平均查找长度
void CompASL(HashTable ha, int m)
{
	int i;
	int s = 0, n = 0;
	for (i = 0; i < m; i++)
	{
		if (ha[i].key != DELKEY&&ha[i].key != NULLKEY)
		{
			s = s + ha[i].count;
			n++;
		}
	}
	printf("查找成功的ASL=%.3f\n", s*1.0 / n);
}
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);//创建哈希函数将x[]中的数组一个一个插入哈希表中,n代表数组非零元素的多少,m是数组的大小,p是哈希除数
	printf("\n");
	DispHT(ha, n, m);//显示哈希搜索的数据
	i = SearchHT(ha, p, k);//找29
	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);//这一步是为了判断k是否删除
	if (i != -1)
	{
		printf("ha[%d].key=%d\n", i, k);
	}
	else
	{
		printf("未找到%d\n",k);
	}
	InsertHT(ha, n, k, p);//然后再将k=77插入哈希表
	DispHT(ha, n, m);
	printf("\n");
	system("pause");
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值