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