哈希表(2)

在上一篇博客中,我们实现了一种哈希表,现在我们基于哈希桶来实现哈希表的基本操作。

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;
}

结果演示:

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值