哈希桶/开散列法实现哈希表

哈希桶/开散列法实现哈希表

        关于开散列的实现思想见该博客——点击打开链接,这里不再介绍,直接上代码。

1. 哈希表结构实现

//哈希桶存放数据
#pragma once

#include <stdio.h>
#include <stdlib.h>                                                                                                                                                     

#define SHOW_NAME printf("\n====================%s====================\n", __FUNCTION__);
#define HASHMAXSIZE 1000

typedef int KeyType;
typedef int ValType;
typedef size_t (*HashFunc)(KeyType key);//函数指针,控制元素存入位置

typedef struct HashElem//相当于链表的结点
{
    KeyType key;
    ValType val;
    struct HashElem* next;//指向下一个元素
}HashElem;

typedef struct HashTable
{
    HashElem* data[HASHMAXSIZE];//哈希表不带头结点,所以用HashElem*型;若哈希表带头结点,用HashElem型,与链表原理一样
    HashFunc func;
    size_t size;//表示当前哈希表中有效元素的个数
}HashTable;

    其中哈希表中每个元素存放一个结点的指针,用以表示整个链表,函数指针用来调用哈希函数控制存入位置,size用来统计当前哈希表中的有效元素个数。每个链表中的每个结点由一个键值对、一个指向下一个结点的next指针构成。

2.初始化

//1.初始化
size_t HashFuncDefault(KeyType key)//                                                                                                                                   
{
    return key%HASHMAXSIZE;
}
void HashInit(HashTable* ht, HashFunc func)//初始化
{
    if(ht == NULL)
        return;
    ht->size = 0;
    ht->func = func;
    size_t i = 0;
    for(i=0; i<HASHMAXSIZE; ++i)
    {   
        ht->data[i] = NULL;
    }   
    return;
}

3. 销毁

//2.销毁
void ElemDestroy(HashElem* node)//销毁结点
{
    free(node);
    return;
}

void HashDestroy(HashTable* ht)//销毁哈希表
{
    if(ht == NULL)
        return;
    ht->size = 0;
    ht->func = NULL;
    size_t i = 0;
    for(i=0; i<HASHMAXSIZE; ++i)
    {
        HashElem* cur = ht->data[i];
        while(cur != NULL)
        {
            HashElem* next = cur->next;
            ElemDestroy(cur);
            cur = next;
        }
    }                                                                                                                                                                   
    return;
}

4. 打印函数,用于测试

        这里注意只打印不为空的链表。

//3.打印函数,用于测试
void HashPrint(HashTable* ht, const char* msg)//打印函数
{
    printf("[%s]\n", msg);
    if(ht == NULL)
        return;
    size_t i = 0;
    for(; i<HASHMAXSIZE; ++i)
    {
        if(ht->data[i] == NULL)
            continue;
        printf("i = %d\n", i);
        HashElem* cur = ht->data[i];
        while(cur != NULL)
        {
            printf("[%d:%d] ", cur->key, cur->val);
            cur = cur->next;
        }
        printf("\n");
    }
    return;
}

5. 创建链表的新结点

//4.创建链表的一个结点
HashElem* ElemCreate(KeyType key, ValType val)
{
    HashElem* new_node = (HashElem*)malloc(sizeof(HashElem));
    new_node->key = key;
    new_node->val = val;
    new_node->next = NULL;
    return new_node;
}

6. 插入操作

(1)根据key值计算插入位置offset

(2)在offset对应的链表找要插入的key

(3)在链表中找到了要插入的key,我们约定为插入失败

(4)若没有要插入的key,就将元素插入到链表中

(5)插入结束后++size

//5.插入操作
HashElem* HashBucketFind(HashElem* head, KeyType key)//查找链表中是否存在当前要插入的元素
{
    HashElem* cur = head;
    while(cur != NULL)
    {
        if(key == cur->key)
            return cur;
        cur = cur->next;
    }
    return NULL;
}

void HashInsert(HashTable* ht, KeyType key, ValType val)//插入
{
    if(ht == NULL)//非法输入
        return;
    //1.根据key计算插入位置offset
    size_t offset = ht->func(key);
    //2.在offset对应链表查找要插入的key
    HashElem* ret = HashBucketFind(ht->data[offset], key);
    //3.若key存在,插入失败
    if(ret != NULL)
        return;
    //4.若key不存在,可进行插入操作(头插)
    HashElem* new_node = ElemCreate(key, val);
    new_node->next = ht->data[offset];
    ht->data[offset] = new_node;
    //5.插入结束,++size
    ++ht->size;                                                                                                                                                         
    return;
}
7. 查找操作

(1)根据key值计算当时的插入位置offset

(2)在offset对应的链表查找key

(3)在链表中找到了查找的key,返回对应的val,查找成功

(4)若链表中没有查找的key,查找失败

//5.给定一个key,查找对应的val
int HashFind(HashTable* ht, KeyType key, ValType* val)//查找
{
    if(ht == NULL || val == NULL)//非法操作
        return 0;
    if(ht->size == 0)//哈希表为空
        return 0;
    //1.根据key计算offset
    size_t offset = ht->func(key);
    //2.在offset对应的链表中遍历查找当前要查找的元素
    HashElem* ret = HashBucketFind(ht->data[offset], key);
    if(ret == NULL)
        return 0;
    *val = ret->val;
    return 1;
}
8. 删除操作

(1)根据key值计算当时的插入位置offset

(2)在offset对应的链表查找key

(3)在链表中找到了要删除的key,删除对应的结点,删除成功

(4)若链表中没有查找的key,删除失败

(5)删除结束后,--size

//6.给定一个key,删除对应元素
int HashBucketFindEx(HashElem* head, KeyType to_find, HashElem** pre, HashElem** to_delete)//查找要删除元素及其父结点
{                                                                                                                                                                       
    if(head == NULL || pre == NULL || to_delete == NULL)//非法输入
        return 0;
    HashElem* ret = head;
    HashElem* ex = NULL;
    for(; ret!=NULL; ret=ret->next)
    {
        if(ret->key == to_find)
            break;
    }
    if(ret == NULL)
        return 0;
    *pre = ex;
    *to_delete = ret;
    return 1;
}
void HashRemove(HashTable* ht, KeyType key)//删除操作
{
    if(ht == NULL)//非法操作
        return;
    if(ht->size == 0)//空哈希表
        return;
    //1.根据key计算offset
    size_t offset = ht->func(key);
    //2.在offset对应链表查找要删除元素
    HashElem* pre;
    HashElem* to_remove;
    int ret = HashBucketFindEx(ht->data[offset], key, &pre, &to_remove);
    //3.没找到要删除元素
    if(ret == 0)
        return;
    //4.要删除元素为链表首元素结点
    if(pre == NULL)
        ht->data[offset] = to_remove->next;
    //5.要删除元素为链表中一个结点
    else
        pre->next = to_remove->next;
    ElemDestroy(to_remove);
    //6.删除操作结束后--size
    --ht->size;
    return;
}

以上函数的测试代码与线性探测法实现的哈希表的测试代码类似。



猜你喜欢

转载自blog.csdn.net/lycorisradiata__/article/details/80409853