该代码实现了一个哈希表,使用拉链法(链地址法)来解决哈希冲突,核心思想是通过链表存储哈希冲突的数据。哈希表的大小被设置为 MAX_SIZE
,其中哈希函数采用除留余数法。以下是代码的详细解释和总结:
#include <stdio.h>
#include <malloc.h>
// 定义常量和类型
#define MAX_SIZE 10 // 哈希表的最大大小
#define ElemType int // 哈希表中存储的数据类型为整数
// 定义节点类型 - 链表结构,表示每个哈希表槽位中的链表节点
struct hashListNode {
ElemType data; // 存储数据
struct hashListNode* next; // 指向下一个节点的指针
};
// 定义一个指针数组,表示哈希表,数组大小为 MAX_SIZE
typedef struct hashListNode* hashTable[MAX_SIZE];
// 初始化哈希表,所有槽位的指针初始化为 NULL
void initHash(hashTable pst) {
for (int i = 0; i < MAX_SIZE; i++) {
pst[i] = NULL; // 初始化每个槽位为空
}
}
// 哈希函数 - 除留余数法,将数据 x 映射到 0 到 MAX_SIZE-1 的范围
int Hash(ElemType key) {
return key % MAX_SIZE; // 计算哈希值
}
// 插入数据到哈希表中
void inserHash(hashTable h, ElemType x) {
// 计算哈希值 (idx) 作为插入的位置
int idx = Hash(x);
// 创建一个新节点,使用头插法将数据插入链表
struct hashListNode* s = (struct hashListNode*)malloc(sizeof(struct hashListNode)); // 分配内存
s->data = x; // 设置节点数据
s->next = h[idx]; // 将当前链表的头节点链接到新节点的 next
h[idx] = s; // 新节点成为链表的头节点
}
// 打印整个哈希表
void prinhashtable(hashTable h) {
for (int i = 0; i < MAX_SIZE; i++) {
printf("%d: ", i); // 打印哈希表的索引
struct hashListNode* p = h[i];
// 打印链表中的每个元素
while (p != NULL) {
printf("%d --> ", p->data);
p = p->next; // 移动到链表的下一个节点
}
printf("NULL\n");
}
}
// 查找哈希表中的数据
struct hashListNode* findHash(hashTable h, ElemType x) {
int idx = Hash(x); // 计算哈希值 (查找的位置)
struct hashListNode* p = h[idx]; // 获取链表的头节点
// 遍历链表查找目标数据
while (p != NULL && p->data != x) {
p = p->next;
}
// 返回找到的节点指针,如果没找到则返回 NULL
return p;
}
// 主函数 - 测试哈希表功能
int main() {
hashTable ht; // 定义哈希表
initHash(ht); // 初始化哈希表
// 插入一组数据
int ar[] = {53, 17, 78, 9, 45, 65, 87, 23};
int n = sizeof(ar) / sizeof(ar[0]); // 计算数组大小
// 将数组中的数据插入哈希表
for (int i = 0; i < n; i++) {
inserHash(ht, ar[i]);
}
// 打印哈希表
prinhashtable(ht);
// 查找数据 45 并打印结果
struct hashListNode* result = findHash(ht, 45);
if (result != NULL) {
printf("%d \nYes\n", result->data); // 找到数据则输出
} else {
printf("Not found\n");
}
return 0;
}
代码逻辑总结
哈希表初始化:
- 哈希表的每个槽位都是一个指向链表头节点的指针,初始时所有槽位都为
NULL
。
哈希函数:
- 使用简单的除留余数法
key % MAX_SIZE
来计算哈希值,将键映射到哈希表的一个槽位。
插入数据:
- 每次插入新数据时,首先通过哈希函数计算其哈希值,确定在哪个槽位插入。
使用头插法将新数据插入该槽位的链表中,即将新节点插入链表的最前面。
第一次:s->next = h[idx]; next h[idx] = s;
之后:
s->next = h[idx]; s的next指针指向h[idx] 所指向的。
赋值操作:赋存储的值。
h[idx] =s ;指向的地址变为s
打印哈希表:
- 打印每个槽位中的链表内容,格式为
data -->
直到NULL
,表示链表的结束。
查找数据:
- 通过哈希函数确定查找的槽位,然后遍历链表寻找目标数据。
- 返回找到的节点或
NULL
表示数据不存在。
拉链法的优势
使用链地址法(拉链法),可以有效解决哈希表中的冲突问题。当多个键映射到相同槽位时,它们会形成一个链表,通过线性查找找到目标数据。这种方法相对简单,且能够动态应对不同数量的冲突数据。