1.基本了解
哈希
:数据与内存一一映射关系,该结构通过key值映射的位置去寻找存放值的地方。
但是哈希表由于是基于数组的,所以扩容的成本大。
哈希地址
:抽象的参照地址,如数组下标。
eg:int arrHash[5]={0};映射关系为y=x(直接定址法)
指数字1存放在arrHash[1]位置上依次类推
数字1000要存在arrHash[1000]比较浪费内存
映射关系(哈希函数)
:可以简单理解为数学中的函数关系,当然也可以自己写一个函数作为映射
eg:
int getHash(char*str)
{
if(strcmp(str,"aa")==0)
{
return 0;0代表aa的哈希地址
}
else if(strcmp(str,"ab")==0)
{
return 1;
}
.....
}
哈希表
:类似于查字典,通过拼音或者部首来在字典中找到对应的字
它会提供哈希里面用到的哈希函数
哈希冲突
eg:int arrHash[10]={0};映射关系为y=x%10
这样只会存放在0-9的位置上
但这样数据1和数据11都会存放在arrHash[1]导致数据冲突,这种现象称为哈希冲突
哈希冲突常见的处理方案
1.开放地址法:当遇到哈希冲突时,数据放在可以存放数据的位置

注意:这种情况下,哈希地址个数要大于元素个数,确保所有元素都可以储存
2.数组链表:当发生哈希冲突时,会以冲突位置为表头创造链表

链式哈希
数据存放在链式结构,而不是数组结构。这种链式结构通常是有序的

键(关键字)
当数据不满足数据结构需求时,要自己构造数据
eg:当我们储存的数据不是整形,例如字符串时,我们要封装一个key来为构建哈希地址做准备
struct pair
{
int key;//键(关键字)
char arr[10];//数据
};
2.例子模拟
这里储存的数据为学生的学号信息与名字
这里将学号作为键,来储存名字
因为每个学号对应一个姓名,所以当遇到相同的学号(键)时要覆盖原来的数据
当发生哈希冲突的时候采用开放地址法,找没有放数据的位置存放
哈希构造函数选用y=x%p,可知p为可以存放数据的多少
思路
学生数据pair为
typedef struct pair
{
int key;//键值
char name[10];//数据
}pair;
用数组实现,当数组对应位置为空的时候用NULL表示
所以这个数组里面要存放的是学生数据的地址
typedef struct HashTable
{
int size;
int capcity;//可存放数据的大小
pair** Hash;//指向一个数组,数组存放的是pair*
}HashTable;
在创建hash时就要对其数组内容进行初始化
HashTable* CreatHash(int Cap)
{
HashTable* NewHash = (HashTable*)malloc(sizeof(HashTable));
NewHash->capcity = Cap;
NewHash->size = 0;
NewHash->Hash = (pair**)malloc(sizeof(pair*) * Cap);
for (int i = 0; i < Cap; i++)
{
NewHash->Hash[i] = NULL;//初始化为空
}
return NewHash;
}
再者,插入数据时要找到对应的hash地址,所以先设计FinPos函数
输入hash指针和对应的键,返回对应位置的pos
这里有3种情况
1.对应位置是空的,或者对应位置已满,但还有空位
2.输入数据键相同,要做数据替换
3.表满了无法插入
//找hash地址
int FindPos(HashTable* dev, int keyD)
{
int pos = keyD % (dev->capcity);
int FinPos = pos;
//当发生哈希冲突时找到没有存放数据的位置,当键相同时覆盖
if (dev->Hash[pos] != NULL && dev->Hash[pos]->key == keyD)//有数据且键相同
{
return pos;
}
do
{
if (dev->Hash[FinPos] == NULL)//如果是空位置
{
return FinPos;
}
FinPos = (FinPos + 1) % (dev->capcity);//看下一个位置是否为空
} while (FinPos != pos);//当FinPos==pos时说明循环一圈都没有空位,这时候返回pos到函数外判断
return FinPos;
}
插入函数:
注意:返回了要插入的位置,这个位置开始是NULL,没有储存数据的空间,这里要先开辟一块pair空间在把数据拷贝过去
当键相同时,只要替换pair结构体中的name数组就行
//插入数据
void Insert(HashTable* dev, pair val)
{
int pos = FindPos(dev, val.key);
if (dev->Hash[pos] == NULL)//返回有空位时
{
//先申请内存,在用memcpy拷贝
dev->Hash[pos] = (pair*)malloc(sizeof(pair));
memcpy(dev->Hash[pos], &val, sizeof(pair));
dev->size++;
}
else//返回没有空位时,可能是key重复,也可能是存满了
{
if (dev->Hash[pos]->key == val.key)//key重复,只要交换数组就行,不用memcpy
{
strcpy(dev->Hash[pos]->name, val.name);
}
else//存满了
{
printf("内存不足,无法插入\n");
}
}
}
打印hash
//打印hsah
void PrintHash(HashTable* dev)
{
for (int i = 0; i < dev->capcity; i++)
{
if (dev->Hash[i] == NULL)
{
printf("NULL \n");
}
else
{
printf("学号%d 姓名%s\n",dev->Hash[i]->key,dev->Hash[i]->name);
}
}
}
3代码实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct pair
{
int key;//键值
char name[10];//数据
}pair;
typedef struct HashTable
{
int size;
int capcity;//可存放数据的大小
pair** Hash;//指向一个数组,数组存放的是pair*
}HashTable;
HashTable* CreatHash(int Cap)
{
HashTable* NewHash = (HashTable*)malloc(sizeof(HashTable));
NewHash->capcity = Cap;
NewHash->size = 0;
NewHash->Hash = (pair**)malloc(sizeof(pair*) * Cap);
for (int i = 0; i < Cap; i++)
{
NewHash->Hash[i] = NULL;//初始化为空
}
return NewHash;
}
//找hash地址
int FindPos(HashTable* dev, int keyD)
{
int pos = keyD % (dev->capcity);
int FinPos = pos;
//当发生哈希冲突时找到没有存放数据的位置,当键相同时覆盖
if (dev->Hash[pos] != NULL && dev->Hash[pos]->key == keyD)//有数据且键相同
{
return pos;
}
do
{
if (dev->Hash[FinPos] == NULL)//如果是空位置
{
return FinPos;
}
FinPos = (FinPos + 1) % (dev->capcity);//看下一个位置是否为空
} while (FinPos != pos);//当FinPos==pos时说明循环一圈都没有空位,这时候返回pos到函数外判断
return FinPos;
}
//插入数据
void Insert(HashTable* dev, pair val)
{
int pos = FindPos(dev, val.key);
if (dev->Hash[pos] == NULL)//返回有空位时
{
//先申请内存,在用memcpy拷贝
dev->Hash[pos] = (pair*)malloc(sizeof(pair));
memcpy(dev->Hash[pos], &val, sizeof(pair));
dev->size++;
}
else//返回没有空位时,可能是key重复,也可能是存满了
{
if (dev->Hash[pos]->key == val.key)//key重复,只要交换数组就行,不用memcpy
{
strcpy(dev->Hash[pos]->name, val.name);
}
else//存满了
{
printf("内存不足,无法插入\n");
}
}
}
//打印hsah
void PrintHash(HashTable* dev)
{
for (int i = 0; i < dev->capcity; i++)
{
if (dev->Hash[i] == NULL)
{
printf("NULL \n");
}
else
{
printf("学号%d 姓名%s\n",dev->Hash[i]->key,dev->Hash[i]->name);
}
}
}
//测试
int main()
{
HashTable* Class = CreatHash(5);
pair Stu[4] = { {1,"小红"},{11,"小刚"},{3,"小明"},{4,"小丽"} };
for (int i = 0; i < 4; i++)
{
Insert(Class, Stu[i]);
}
PrintHash(Class);
printf("\n");
pair Stu1 = { 21,"小华" };//正常插入
Insert(Class, Stu1);
PrintHash(Class);
printf("\n");
pair Stu2 = { 1,"丽丽" };//重复覆盖
Insert(Class, Stu2);
PrintHash(Class);
printf("\n");
pair Stu3 = { 31,"罗斯" };//验证已满
Insert(Class, Stu3);
PrintHash(Class);
return 0;
}

如果对你有帮助的话还请点赞多多支持一下,一起加油学习吧
8710





