哈希表(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");}
}
这就是文章的全部的内容了,希望对你有所帮助,如有错误欢迎指出。