哈希表(Hash Table)是一种高效的数据结构,能够在平均情况下实现常数时间的查找、插入和删除操作。
哈希表的核心是哈希函数,哈希函数是一个将输入数据(通常称为“键”或“key”)转换为固定长度的整数的函数。这个整数通常用作哈希表(Hash Table)中的索引,用于快速查找数据。本质上就是根据输入的值确定值的位置。
例如,输入除以10取余是位置编号:
int HashPosFind(int num) {
return num % 10;
}
如果有重复的情况呢,那么要在其他位置存储,有不同的方法,这里采取的是链表法。
如图所示,如果序号为1的地址处已经有值存在,那么我们就把新的数连接到链表上。
首先我们可以写出类似于这样的函数:
#define NUM 10
#define Nan -32767
typedef struct Seqlist {
struct Seqlist* next;
int val;
}Seqlist;
typedef struct HashList {
int num;
Seqlist** list;
}HashList;
HashList* HashInit() {
HashList* H = (HashList*)malloc(sizeof(HashList));
H->num = 0;
H->list = (Seqlist**)malloc(sizeof(Seqlist*) *NUM);
for (int i = 0; i < NUM; i++) {
H->list[i] = (Seqlist*)malloc(sizeof(Seqlist));
H->list[i]->next = NULL;
}
for (int i = 0; i < NUM; i++) {
H->list[i]->val = Nan;
}
return H;
}
1.首先定义NUM表示一共有十个位置定义链表,定义Nan表示没有数的时候的数值。
2.然后定义哈希表的结构,用二级指针的方法模拟二维数组,准确的说是二维链表。
3.接着为哈希表开辟空间,先为10个链表指针开辟二级指针(指针数组),接着在为每个序列号指针开辟空间并且初始化为Nan表示此时哈希表为空。
接着就是哈希表插入操作,这里要考虑两种情况,第一是当取得地址序列号之后,此时的链表存储的是Nan,那我们要先把链表的Nan赋值为插入的数据,然后新创建一个节点存储Nan表示链表的结尾。
第二种情况的是第一个数值不是Nan,这时要一直找到链表的尾,然后同样执行上一步的操作:先赋值为插入的数值,然后更新链表的尾。
代码如下:
void HashPush(int val,HashList* H) {
int pos = HashPosFind(val);
if (H->list[pos]->val == Nan) {
H->list[pos]->val = val;
Seqlist* new_node = (Seqlist*)malloc(sizeof(Seqlist));
new_node->val = Nan;
new_node->next = NULL;
H->list[pos]->next = new_node;
}
else {
printf("重新插入中\n");
int num = 1;
Seqlist* cur = H->list[pos];
while (cur->val != Nan) {
cur = cur->next;
}
Seqlist* new_node = (Seqlist*)malloc(sizeof(Seqlist));
new_node->val = Nan;
new_node->next = NULL;
cur->next = new_node;
cur->val = val;
}
}
首先是第一种情况,因为是Nan所以说明此时的链表为空,所以直接存储,然后开辟一个新的节点存储Nan表示链表的结尾。
第二种情况说明已经有值存在,这时先找到链表的尾(找到节点值为Nan的节点),然后执行和第一步一样的操作:先存储值,然后新开辟一个节点存储Nan。
最后是打印函数:思路比较简单,一直打印到Nan为止。
void HashPrint(HashList* H) {
for (int i = 0; i < NUM; i++) {
printf("%d: ",i);
Seqlist* cur = H->list[i];
while (cur->val != Nan) {
printf("%d ", cur->val);
cur = cur->next;
}
printf("\n");
}
}
这就是文章的全部的内容了,希望对你有所帮助,如有错误欢迎指出。