哈希表也称散列表,是根据关键码值而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。哈希表的查找速度非常快,几乎是O(1)的时间复杂度。
下面我们实现的哈希表期望存储的数据是键值对的结构。
头文件:
hash.h:
#pragma once #include<stdio.h> #include<stddef.h> #define HashMaxSize 1000 typedef int KeyType; typedef int ValType; typedef enum{ Empty, Valid, Deleted, }Stat; typedef struct HashElem{ KeyType key; ValType value; Stat stat; }HashElem; typedef size_t (*HashFunc)(KeyType key); typedef struct HashTable{ 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);
- 初始化
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].stat = Empty; } return; }
- 销毁
void HashDestroy(HashTable* ht){ if(ht == NULL) return; ht->size = 0; ht->func = NULL; return; }
- 插入
void HashInsert(HashTable* ht,KeyType key,ValType value){ if(ht == NULL)//非法输入 return; if(ht->size >= 0.8*HashMaxSize)//判定hash表能否继续插入(根据负载因子判定),这里我们假定负载因子为0.8 return; size_t offset = ht->func(key);//根据key计算offset //从offset位置开始线性的往后查找,找到第一个状态为Empty的元素来进行插入 while(1){ if(ht->data[offset].stat != Valid){ //此时找到了一个合适的位置来放置要插入的元素 ht->data[offset].stat = Valid; ht->data[offset].key = key; ht->data[offset].value = value; //++size ++ht->size; return; } else if(ht->data[offset].stat == Valid && ht->data[offset].key == key){ //hash表中存在了一个key相同的元素,此时我们单纯的认为插入失败 return; } else{ ++offset; if(offset >= HashMaxSize) offset = 0; } } //如果发现了key相同的元素,此时认为插入失败 return; }
- 查找
int HashFind(HashTable* ht,KeyType key,ValType* value){ if(ht == NULL || value == NULL) return 0; if(ht->size == 0) return 0; size_t offset = ht->func(key);//根据key计算出offset while(1){//从offset开始往后进行查找,每次取得一个元素,使用key进行比较 //如果找到了key相同的元素,此时直接把value返回,并且认为查找成功 if(ht->data[offset].key == key && ht->data[offset].stat == Valid){ //找到了 *value = ht->data[offset].value; return 1; } else if(ht->data[offset].stat == Empty) //如果发现当前元素是一个空元素,此时认为查找失败 return 0; else{ //如果发现当前的key不相同,就继续往后查找 ++offset; offset = offset>=HashMaxSize?0:offset; } } return 0; }
- 删除
void HashRemove(HashTable* ht,KeyType key){ if(ht == NULL) return; if(ht->size == 0) return; size_t offset = ht->func(key);//根据key计算offset //从offset开始,依次判定当前元素的key和要删除元素的key是否相同 while(1){ //若当前的key就是要删除的key,删除当前元素即可,将要删除元素的状态标记为Deleted if(ht->data[offset].key == key && ht->data[offset].stat == Valid){ ht->data[offset].stat = Deleted; --ht->size; return; } //若当前元素为空元素,则key在hash表中没有找到,删除失败,直接返回即可 else if(ht->data[offset].stat == Empty) return; else{ ++offset; offset = offset>=HashMaxSize?0:offset; } } return; }
- 哈希表打印
void HashPrint(HashTable* ht,const char* msg){ printf("[%s]\n",msg); size_t i = 0; for(;i < HashMaxSize;++i){ if(ht->data[i].stat == Empty){ continue; } printf("[%lu %d:%d] ",i,ht->data[i].key,ht->data[i].value); } printf("\n"); }
- 默认函数
size_t HashFuncDefault(KeyType key){ return key % HashMaxSize; }
测试函数:
#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; }
结果演示: