在上一篇博客中,我们实现了一种哈希表,现在我们基于哈希桶来实现哈希表的基本操作。
hash.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stddef.h>
#define HashMaxSize 1000
typedef int KeyType;
typedef int ValType;
typedef struct HashElem{
KeyType key;
ValType value;
struct HashElem* next;
}HashElem;
typedef size_t(*HashFunc)(KeyType key);
typedef struct HashTable{
//如果我们的hash桶上面的链表是一个不带头节点的链表,类型就用HashElem*
//如果我们的hash桶上面的链表是一个带头节点的链表,类型就用HashElem
HashElem* data[HashMaxSize];
size_t size;
HashFunc func;
}HashTable;
size_t HashFuncDefault(KeyType key);
void HashPrint(HashTable* ht,const char* msg);
void HashInit(HashTable* ht,HashFunc hash_func);
void HashDestroy(HashTable* ht);
void HashInsert(HashTable* ht,KeyType key,ValType value);
int HashFind(HashTable* ht,KeyType key,ValType* value);
void HashRemove(HashTable* ht,KeyType key);
- 默认函数
size_t HashFuncDefault(KeyType key){
return key % HashMaxSize;
}
- 创建节点
HashElem* CreateElem(KeyType key,ValType value){
HashElem* new_node = (HashElem*)malloc(sizeof(HashElem));
new_node->key = key;
new_node->value = value;
new_node->next = NULL;
return new_node;
}
- 销毁节点
void DestroyElem(HashElem* node){
free(node);
}
- 初始化哈希表
void HashInit(HashTable* ht,HashFunc hash_func){
if(ht == NULL)
return;
ht->size = 0;
ht->func = hash_func;
size_t i = 0;
for(;i < HashMaxSize;++i){
ht->data[i] = NULL;
}
return;
}
- 销毁哈希表
void HashDestroy(HashTable* ht){
if(ht == NULL)
return;
ht->size = 0;
ht->func = NULL;
//遍历所有链表,进行释放操作
size_t i = 0;
for(;i < HashMaxSize;i++){
HashElem* cur = ht->data[i];
while(cur != NULL){
HashElem* next = cur->next;
DestroyElem(cur);
cur = next;
}
}
return;
}
- 打印哈希表
void HashPrint(HashTable* ht,const char* msg){
printf("[%s]\n",msg);
size_t i = 0;
for(;i < HashMaxSize;++i){
if(ht->data[i] == NULL)
continue;
printf("i = %lu\n",i);
HashElem* cur = ht->data[i];
for(;cur != NULL;cur = cur->next){
printf("[%d:%d]",cur->key,cur->value);
}
printf("\n");
}
}
- 插入
HashElem* HashBucketFind(HashElem* head,KeyType to_find){
HashElem* cur = head;
for(;cur != NULL;cur = cur->next){
if(cur->key == to_find)
break;
}
return cur;
}
void HashInsert(HashTable* ht,KeyType key,ValType value){
if(ht == NULL)
return;
//根据key值计算offset
size_t offset = ht->func(key);
//在offset对应的链表中查找看当前的key是否存在
//若存在,就认为插入失败
HashElem* ret = HashBucketFind(ht->data[offset],key);
if(ret != NULL)//此时说明存在了重复的key值,认为插入失败
return;
//若不存在,就使用头插进行插入
HashElem* new_node = CreateElem(key,value);
new_node->next = ht->data[offset];
ht->data[offset] = new_node;
//++size
++ht->size;
return;
}
- 查找
int HashFind(HashTable* ht,KeyType key,ValType* value){
if(ht == NULL || value == NULL)
return 0;
//根据key计算offset
size_t offset = ht->func(key);
//找到对应offset的链表,遍历链表尝试找到其中的元素
HashElem* ret = HashBucketFind(ht->data[offset],key);
if(ret == NULL)
return 0;
*value = ret->value;
return 1;
}
- 删除
int HashBucketFindEx(HashElem* head,KeyType to_find,HashElem** pre_node,HashElem** cur_node){
HashElem* cur = head;
HashElem* pre = NULL;
for(;cur != NULL;pre = cur,cur = cur->next){
if(cur->key == to_find)
break;
}
if(cur == NULL)
return 0;
*pre_node = pre;
*cur_node = cur;
return 1;
}
void HashRemove(HashTable* ht,KeyType key){
if(ht == NULL)
return;
if(ht->size == 0)
return;
//根据key计算offset
size_t offset = ht->func(key);
//通过offset找到对应的链表,
//在链表中找到指定元素并进行删除
HashElem* pre = NULL;
HashElem* cur = NULL;
int ret = HashBucketFindEx(ht->data[offset],key,&pre,&cur);
if(ret == 0)//未找到,删除失败
return;
if(pre == NULL)//要删除的节点是链表的头结点
ht->data[offset] = cur->next;
else{
//要删除的元素不是链表头节点
pre->next = cur->next;
}
DestroyElem(cur);
//--size;
--ht->size;
return;
}
测试函数:
#include"hash.h"
#define PRINT_HEAD printf("\n============%s============\n",__FUNCTION__);
void TestInit(){
PRINT_HEAD;
HashTable ht;
HashInit(&ht,HashFuncDefault);
printf("size expect 0,actual %lu\n",ht.size);
printf("func expect %p,actual %p\n",HashFuncDefault,ht.func);
return;
}
void TestDestroy(){
PRINT_HEAD;
HashTable ht;
HashInit(&ht,HashFuncDefault);
HashDestroy(&ht);
printf("size expect 0,actual %lu\n",ht.size);
printf("func expect NULL,actual %p\n",ht.func);
return;
}
void TestInsert(){
PRINT_HEAD;
HashTable ht;
HashInit(&ht,HashFuncDefault);
HashInsert(&ht,1,1);
HashInsert(&ht,1,10);
HashInsert(&ht,2,2);
HashInsert(&ht,1001,11);
HashInsert(&ht,1002,12);
HashPrint(&ht,"插入若干元素");
return;
}
void TestFind(){
PRINT_HEAD;
HashTable ht;
HashInit(&ht,HashFuncDefault);
HashInsert(&ht,1,1);
HashInsert(&ht,1,10);
HashInsert(&ht,2,2);
HashInsert(&ht,1001,11);
HashInsert(&ht,1002,12);
ValType value;
int ret = HashFind(&ht,1002,&value);
printf("ret expect 1,actual %d\n",ret);
printf("value expect 12,actual %d\n",value);
ret = HashFind(&ht,3,&value);
printf("ret expect 0,actual %d\n",ret);
}
void TestRemove(){
PRINT_HEAD;
HashTable ht;
HashInit(&ht,HashFuncDefault);
HashInsert(&ht,1,1);
HashInsert(&ht,1,10);
HashInsert(&ht,2,2);
HashInsert(&ht,1001,11);
HashInsert(&ht,1002,12);
HashRemove(&ht,2);
ValType value;
int ret = HashFind(&ht,1002,&value);
printf("ret expect 1,actual %d\n",ret);
printf("value expect 12,actual %d\n",value);
ret = HashFind(&ht,2,&value);
printf("ret expect 0,actual %d\n",ret);
}
int main(){
TestInit();
TestDestroy();
TestInsert();
TestFind();
TestRemove();
return 0;
}
结果演示: