在上一篇博客中,我们实现了一种哈希表,现在我们基于哈希桶来实现哈希表的基本操作。
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; }结果演示: