1. 结构体定义
typedef struct pair {
int key; // 哈希表中的键,通常是整数类型
char element[20]; // 关联的字符串元素,最多存储20个字符
} DATA, * LPDATA;
pair
结构体表示哈希表中的一个元素,包含两个字段:
key
:这是一个整数,作为哈希表的键,通常用于查找或存储值。element
:一个字符数组,用来存储与key
关联的字符串数据。这个字段的大小为 20,这意味着每个元素最多可以存储 19 个字符(最后一个字符用于存储字符串的结束符'\0'
)。
typedef struct hashTable {
LPDATA* table; // 一个指针数组,存储哈希表的桶,每个桶包含一个指向 DATA 的指针
int divisor; // 哈希表的大小,决定了桶的数量
int curSize; // 当前哈希表中存储的元素数量
} HASH, * LPHASH;
hashTable
结构体代表哈希表本身,包含以下字段:
table
:指向一个LPDATA
类型的指针数组。每个LPDATA
是一个指向DATA
结构体的指针,表示哈希表中的一个桶。divisor
:哈希表的大小,决定了哈希表中桶的数量。curSize
:当前哈希表中已经存储的元素数量。
2. 创建哈希表函数 createHashTable
LPHASH createHashTable(int p) {
LPHASH hash = (LPHASH)malloc(sizeof(HASH)); // 分配内存为哈希表结构体
assert(hash != NULL); // 确保内存分配成功
hash->curSize = 0; // 初始化哈希表的当前大小为 0
hash->divisor = p; // 设置哈希表的大小
hash->table = (LPDATA*)malloc(sizeof(LPDATA) * hash->divisor); // 为桶数组分配内存
assert(hash->table != NULL); // 确保内存分配成功
// 初始化哈希表中的所有桶指针为 NULL,表示所有桶都是空的
for (int i = 0; i < hash->divisor; i++) {
hash->table[i] = NULL;
}
return hash; // 返回创建的哈希表指针
}
功能:createHashTable
函数用于创建一个哈希表:
- 使用
malloc
为哈希表结构体分配内存。 - 设置哈希表的大小为
p
,并为桶数组table
分配内存,每个桶存储一个指向DATA
结构体的指针。 - 初始化哈希表中的所有桶为
NULL
,表示它们还没有存储任何元素。 - 返回创建的哈希表指针。
3. 查找函数 search
int search(LPHASH hash, int key) {
int pos = key % hash->divisor; // 计算哈希值,得到该元素应放置的初始位置
int curpos = pos; // 当前的位置初始化为计算得到的初始位置
// 线性探测法查找位置,直到找到空位或已有相同 key 的位置
do {
if (hash->table[curpos] == NULL || hash->table[curpos]->key == key) {
return curpos; // 如果当前位置为空,或者找到了相同的 key,返回当前的位置
}
// 如果当前位置被占用且 key 不相同,采用线性探测法,检查下一个位置
curpos = (curpos + 1) % hash->divisor; // 如果超出了表的末尾,则环绕到表的开始位置
} while (curpos != pos); // 如果回到初始位置,表示表已满
return -1; // 如果哈希表已满,返回 -1
}
功能:search
函数使用线性探测法查找合适的位置插入一个元素或查找一个已有的元素。
- 首先,计算该元素的哈希值,决定该元素应放置的初始位置。
- 然后,使用线性探测法(如果当前位置被占用,检查下一个位置)查找该位置。如果当前位置为空或者找到了相同
key
的元素,返回当前位置。 - 如果遍历完整个哈希表(回到起始位置),说明哈希表已满,返回
-1
。
4. 插入数据函数 insertData
void insertData(LPHASH hash, DATA data) {
// 如果哈希表已满,无法插入新数据
if (hash->curSize >= hash->divisor) {
printf("Hash table is full!\n");
return;
}
// 查找数据的插入位置
int pos = search(hash, data.key);
// 如果返回 -1,表示表已满,无法插入
if (pos == -1) {
printf("Hash table is full, cannot insert new data.\n");
return;
}
// 如果该位置为空,插入数据
if (hash->table[pos] == NULL) {
hash->table[pos] = (LPDATA)malloc(sizeof(DATA)); // 为该位置分配内存
assert(hash->table[pos] != NULL); // 确保内存分配成功
// 将数据复制到该位置
memcpy(hash->table[pos], &data, sizeof(DATA));
hash->curSize++; // 更新当前哈希表大小
}
// 如果该位置已经存在相同的 key,则更新该位置的 element
else if (hash->table[pos]->key == data.key) {
// 使用 strncpy 避免字符串溢出,确保 element 的长度不会超过 19 个字符
strncpy(hash->table[pos]->element, data.element, sizeof(hash->table[pos]->element) - 1);
hash->table[pos]->element[sizeof(hash->table[pos]->element) - 1] = '\0'; // 确保字符串以 '\0' 结尾
}
else {
printf("Key already exists, but handled by rehashing mechanism.\n");
}
}
功能:insertData
函数用于将一个 DATA
数据插入哈希表中:
- 首先检查哈希表是否已满,如果已满,打印提示并返回。
- 使用
search
函数查找插入位置。如果返回-1
,表示哈希表已满,无法插入。 - 如果该位置为空,分配内存存储数据并将数据复制到该位置,并增加当前哈希表大小。
- 如果该位置已存在相同
key
,则使用strncpy
安全地更新该位置的element
字段。
5. 打印哈希表函数 printHash
void printHash(LPHASH hash) {
for (int i = 0; i < hash->divisor; i++) {
if (hash->table[i] == NULL) {
printf("NULL\n"); // 如果桶为空,打印 NULL
}
else {
// 打印当前桶中的 key 和 element
printf("Key:%d Element:%s\n", hash->table[i]->key, hash->table[i]->element);
}
}
}
功能:printHash
函数遍历哈希表中的所有桶,打印每个桶的内容:
- 如果桶为空(即该位置为
NULL
),打印 "NULL"。 - 如果桶不为空,则打印该桶的
key
和element
。
6. 主函数 main
int main() {
// 创建一个大小为 10 的哈希表
LPHASH hash = createHashTable(10);
// 定义一个 DATA 数组,包含 4 个元素
DATA array[4] = { {1, "雷电"}, {11, "春妙"}, {13, "晓月"}, {17, "雪月"} };
// 将数组中的数据插入到哈希表中
for (int i = 0; i < 4; i++) {
insertData(hash, array[i]);
}
// 打印哈希表的内容
printHash(hash);
// 释放哈希表中每个桶存储的数据内存
for (int i = 0; i < hash->divisor; i++) {
if (hash->table[i] != NULL) {
free(hash->table[i]); // 释放每个桶中的 DATA 元素内存
}
}
// 释放存储桶指针数组的内存
free(hash->table);
// 释放哈希表结构体本身的内存
free(hash);
return 0; // 程序执行成功,返回 0
}
- 创建一个大小为 10 的哈希表。
- 定义并插入 4 个
DATA
数据元素到哈希表中。 - 打印哈希表的内容,查看每个桶的
key
和element
。 - 在程序结束时,释放所有分配的内存,包括哈希表元素和哈希表结构本身。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <memory.h>
// 定义一个结构体 'pair',表示哈希表中的一个数据元素
// 包含两个字段:key 和 element
typedef struct pair {
int key; // 哈希表中的键
char element[20]; // 关联的字符串元素,假设最大长度为 20
} DATA, * LPDATA;
// 定义哈希表结构体
typedef struct hashTable {
LPDATA* table; // 存储元素的桶数组,每个桶存储一个 DATA 指针
int divisor; // 哈希表的大小,决定了桶的数量
int curSize; // 当前哈希表中的元素数量
} HASH, * LPHASH;
// 创建一个哈希表
LPHASH createHashTable(int p) {
LPHASH hash = (LPHASH)malloc(sizeof(HASH)); // 为哈希表结构体分配内存
assert(hash != NULL); // 检查内存分配是否成功
hash->curSize = 0; // 初始化哈希表的当前大小为 0
hash->divisor = p; // 设置哈希表的大小
hash->table = (LPDATA*)malloc(sizeof(LPDATA) * hash->divisor); // 为桶数组分配内存
assert(hash->table != NULL); // 检查内存分配是否成功
// 初始化哈希表中的所有桶指针为 NULL,表示所有桶都是空的
for (int i = 0; i < hash->divisor; i++) {
hash->table[i] = NULL;
}
return hash; // 返回创建的哈希表指针
}
// 查找指定 key 的位置
int search(LPHASH hash, int key) {
int pos = key % hash->divisor; // 使用哈希函数计算初始位置
int curpos = pos; // 当前位置初始化为计算得到的初始位置
// 线性探测法查找位置,直到找到空位或已有相同 key 的位置
do {
if (hash->table[curpos] == NULL || hash->table[curpos]->key == key) {
return curpos; // 如果当前位置为空,或者找到了相同的 key,返回当前的位置
}
// 如果当前位置被占用且 key 不相同,采用线性探测法,检查下一个位置
curpos = (curpos + 1) % hash->divisor; // 如果超出了表的末尾,则环绕到表的开始位置
} while (curpos != pos); // 如果回到初始位置,表示表已满
return -1; // 如果哈希表已满,返回 -1
}
// 插入数据到哈希表
void insertData(LPHASH hash, DATA data) {
// 如果哈希表已满,无法插入新数据
if (hash->curSize >= hash->divisor) {
printf("Hash table is full!\n");
return;
}
// 查找数据的插入位置
int pos = search(hash, data.key);
// 如果返回 -1,表示表已满,无法插入
if (pos == -1) {
printf("Hash table is full, cannot insert new data.\n");
return;
}
// 如果该位置为空,插入数据
if (hash->table[pos] == NULL) {
hash->table[pos] = (LPDATA)malloc(sizeof(DATA)); // 分配内存存储数据
assert(hash->table[pos] != NULL); // 确保内存分配成功
// 将数据复制到该位置
memcpy(hash->table[pos], &data, sizeof(DATA));
hash->curSize++; // 更新当前哈希表大小
}
// 如果该位置已经存在相同的 key,则更新该位置的 element
else if (hash->table[pos]->key == data.key) {
// 使用 strncpy 避免字符串溢出,确保 element 的长度不会超过 19 个字符
strncpy(hash->table[pos]->element, data.element, sizeof(hash->table[pos]->element) - 1);
hash->table[pos]->element[sizeof(hash->table[pos]->element) - 1] = '\0'; // 确保字符串以 '\0' 结尾
}
else {
printf("Key already exists, but handled by rehashing mechanism.\n");
}
}
// 打印哈希表的内容
void printHash(LPHASH hash) {
for (int i = 0; i < hash->divisor; i++) {
if (hash->table[i] == NULL) {
printf("NULL\n"); // 如果桶为空,打印 NULL
}
else {
// 打印当前桶中的 key 和 element
printf("Key:%d Element:%s\n", hash->table[i]->key, hash->table[i]->element);
}
}
}
// 主函数
int main() {
// 创建一个大小为 10 的哈希表
LPHASH hash = createHashTable(10);
// 定义一个 DATA 数组,包含 4 个元素
DATA array[4] = { {1, "雷电"}, {11, "春妙"}, {13, "晓月"}, {17, "雪月"} };
// 将数组中的数据插入到哈希表中
for (int i = 0; i < 4; i++) {
insertData(hash, array[i]);
}
// 打印哈希表的内容
printHash(hash);
// 释放哈希表中每个桶存储的数据内存
for (int i = 0; i < hash->divisor; i++) {
if (hash->table[i] != NULL) {
free(hash->table[i]); // 释放每个桶中的 DATA 元素内存
}
}
// 释放存储桶指针数组的内存
free(hash->table);
// 释放哈希表结构体本身的内存
free(hash);
return 0; // 程序执行成功,返回 0
}