C语言简单实现Hashtable

简单说明

hashtable是根据key查询value的一种数据结构,使用数组结构来存储所有的元素,使用一种方式将key映射到数组的不同下标,查询时key就可以直接映射到value,时间复杂度为O(1),因此,hashtable结构经常用于查询的目的。

要实现hashtable,主要有以下2点

1.寻找一种隐射关系,将key映射到数组的某个下标,本文使用简单的取余方式来得
到key在元素中的下标,如果为了hashtable映射更具有一致性
(尽可能的多隐射到不同的下标,少产生冲突),请自行阅读更多的资料
2.不同的key可能会映射到数组的同一个下标,那么如何处理这种冲突,
本文使用的链表来处理冲突。

本文不对理论做过多说明,只用代码说明是如何实现的,如果觉得实现有问题,欢迎拍砖。

实现

头文件

#ifndef LINKED_HASHTABLE_H
#define LINKED_HASHTABLE_H

#include <stdint.h>

typedef void (*free_hashtable_func)(void *); ///< 释放函数指针声明

/**************************************************************************
* @file linked_hashtable.h
* @brief 使用链表方式处理冲突的哈希表
* @author liqiang
* @date 2021.6.8
* @modify 2022.5.25
* @version v1.0
**************************************************************************/
#define CKEYHASHTABLESIZE 8317  /**< hash桶元素的最大值*/

#define  HASH_TABLE_OUT_MEMORY  -1
#define  HASH_TABLE_NO_CUR_ELEMENT -2   //不存在当前元素
#define  HASH_TABLE_NO_CUR_KEY -3    //不存在当前元素的key
#define  HASH_TABLE_NO_EXIST  -4
#define  HASH_TABLE_NO_KEY_ELEMENT   -5 //table中不存在对应key的元素
#define  HASH_TABLE_KEYALREADYEXISTS -6 //table中不能插入key存在的值

struct key_hash_table;
struct key_hash_element;

/**************************************************************************
 * @brief hash_table_new 创建hashtable对象
 * @param table 要创建的table
 * @param cover_old_value[in] 如果为0,则添加时,新值不能覆盖旧值,如果为其他,则可以
 * 覆盖
 * @return 成功创建返回0,否则返回 HASH_TABLE_OUT_MEMORY
 **************************************************************************/
 int hash_table_new(struct key_hash_table **table, int cover_old_value);

/**************************************************************************
 * @brief hash_table_free 清空表的元素和响应的内存空间
 * @param table
 **************************************************************************/
void hash_table_free(struct key_hash_table *table);

/**************************************************************************
 * @brief hash_table_custom_free 释放表及其元素的相关内存,元素的释放工作
 * 由free_func指定
 * @param table 要释放的table
 * @param free_func 释放元素的操作函数指针
 **************************************************************************/
void hash_table_custom_free(struct key_hash_table *table,
                            free_hashtable_func free_func);

/**************************************************************************
 * @brief hash_table_goto_first_element 设置hashtable 指向第一个元素
 * @param table
 **************************************************************************/
void hash_table_goto_first_element(struct key_hash_table *table);


/**************************************************************************
 * @brief hash_table_goto_last_element 设置hashtable 指向最后一个元素
 * @param table
 **************************************************************************/
void hash_table_goto_last_element(struct key_hash_table *table);


/**************************************************************************
 * @brief hash_table_has_cur_element 判断hashtable 当前元素是否存在
 * @param table
 * @return 存在返回1,否则返回0
 **************************************************************************/
int hash_table_has_cur_element(struct key_hash_table *table);

/**************************************************************************
 * @brief hash_table_del_cur_element 删除hashtable 当前元素
 * @param table
 * @return 成功返回0,否则返回小于0的错误码(HASH_TABLE_NO_CUR_ELEMENT)
 **************************************************************************/
int hash_table_del_cur_element(struct key_hash_table *table);

/**************************************************************************
 * @brief hash_table_get_cur_element 返回hashtable 当前元素的value
 * @param table
 * @return 如果table不为空,返回hashtable 当前元素的value,否则返回NULL
 **************************************************************************/
void *hash_table_get_cur_element(struct key_hash_table *table);

/**************************************************************************
 * @brief hash_table_get_cur_key 返回hashtable 当前元素的key
 * @param table
 * @return 如果table存在,返回当前元素的key,否则返回 HASH_TABLE_NO_CUR_KEY
 **************************************************************************/
uint32_t hash_table_get_cur_key(struct key_hash_table *table);

/**************************************************************************
 * @brief hash_table_goto_key_element hashtable当前指针指向hashtable
 * 指定key对应的的元素
 * @param table
 * @param key 指定的key
 * @return 找到返回0,没找到返回错误码
 **************************************************************************/
int hash_table_goto_key_element(struct key_hash_table *table,const uint32_t key);

/**************************************************************************
 * @brief hash_table_has_key_element 根据key判断对应的hash桶的下标是否存在对应的元素
 * @param table[in]
 * @param key[in] 指定的key
 * @return 存在key的元素返回TRUE,不存在对应key返回0
 **************************************************************************/
int hash_table_has_key_element(struct key_hash_table *table, const uint32_t key);

/**************************************************************************
 * @brief hash_table_add_element 往hashtable中添加元素
 * @param table[in]
 * @param key[in] 添加的key
 * @param value[in] 添加的value值
 * @param b_need_free_value[in] 是否需要删除元素值的内存
 * @return 添加成功返回0,否则返回错误码
 **************************************************************************/
int hash_table_add_element(struct key_hash_table* table,const uint32_t key,
                           void *value,int b_need_free_value);

/**************************************************************************
 * @brief hash_table_goto_next_element 获取当前元素的下一个元素
 * @param table[in]
 **************************************************************************/
void hash_table_goto_next_element(struct key_hash_table* table);


/**************************************************************************
 * @brief hash_table_goto_prev_element 获取当前元素的前一个元素
 * @param table[in]
 **************************************************************************/
void hash_table_goto_prev_element(struct key_hash_table* table);


/**************************************************************************
 * @brief hash_table_del_key_element 从hashtable中删除指定key的元素
 * @param table[in]
 * @param key[in] 指定的key
 * @return 成功返回0,否则返回错误码
 **************************************************************************/
int hash_table_del_key_element(struct key_hash_table *table,const uint32_t key);

#endif //

C文件

#include <stdio.h>

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "linked_hashtable.h"

/****************************************************************************
 *brief RtpKeyHashElement为 hashtable的元素结构
**************************************************************************/
typedef struct key_hash_element
{
    int hashindex;  /**< 元素占用的下标位置 */
    int b_need_free_value; /**< 是否需要删除元素value的内存 */
    uint32_t key;  /**< 元素的key(使用无符号32位作为元素的key)*/
    void* value; /**< 元素的value */
    struct key_hash_element *hashprev,*hashnext;  /**< hash桶的前后元素指针(横向列表,不同的key得到相同的hash值,链表连接这些元素) */
    struct key_hash_element *listprev,*listnext; /**< 哈希桶的纵向数组的列表(寻找不同下标的元素) */
}key_hash_element_t;

/**************************************************************************
 *brief RtpKeyHashTabale为 hashtable 结构
**************************************************************************/
typedef struct key_hash_table
{
    key_hash_element_t *element_table[CKEYHASHTABLESIZE]; /**< 指针数组:有CKEYHASHTABLESIZE个指针元素的数组,每个指针指向一个key_hash_element_t特有的元素)*/
    key_hash_element_t *firsthashelem,*lasthashelem; /**< table第一个元素和最后一个元素的指针*/
    key_hash_element_t *curhashelem; /**< 当前元素的指针*/
    int cover_old_value;/**< 添加时,该变量用于记录是否用新值覆盖旧值,1-覆盖,0-不覆盖*/
}key_hash_table;

/**************************************************************
 * @brief getRtpHashTableIndex 根据key获取下标index(hash函数)
 * @param key 元素key
 * @return 返回元素key的index
 **************************************************************/
static inline int get_hash_table_index(const uint32_t key)
{
    return key % CKEYHASHTABLESIZE;
}

int hash_table_new(key_hash_table **table, int cover_old_value)
{
    key_hash_table *temp = *table;
    if(temp != 0) memset(temp,0,0);
    else
    {
        temp = (key_hash_table *)calloc(1, sizeof(key_hash_table));
    }
    if(temp == NULL) return HASH_TABLE_OUT_MEMORY;
    temp->cover_old_value = cover_old_value;
    *table = temp;
    return 0;
}

void hash_table_free(key_hash_table *table)
{
    if(table == NULL) return;

    key_hash_element_t *list_tmp = NULL,*list_tmp_next = NULL;//用于存放hashtable桶的元素:list_tmp从第一个开始,list_tmp用于保存哈希桶下一个元素(用于纵向列表)
    /**
    * 释放hashtable所有元素的内存空间
    */
    list_tmp = table->firsthashelem;
    while(list_tmp != 0)//释放元素内存空间
    {
        key_hash_element_t *hash_tmp,*hash_next_tmp; //用于保存横向hashtable的元素,可能多个不同的key,hash值是一样的,这时候就需要在桶中的同一个元素使用链表标识
        hash_tmp = list_tmp->hashnext;//
        while(hash_tmp != 0)
        {
            hash_next_tmp = hash_tmp->hashnext;
            if(hash_tmp->b_need_free_value) //如果需要删除元素内容空间
            {
                free(hash_tmp->value);
                hash_tmp->value = NULL;
            }
            free(hash_tmp);
            hash_tmp = hash_next_tmp;
        }
        list_tmp_next = list_tmp->listnext;//获取hashtbale的下一个元素
        if(list_tmp->b_need_free_value)
        {
            free(list_tmp->value);
            list_tmp->value = NULL;
        }
        free(list_tmp);
        list_tmp = list_tmp_next;
    }
    table->firsthashelem = 0;
    table->lasthashelem = 0;
    table->curhashelem = 0;

    for (int i = 0 ; i < CKEYHASHTABLESIZE ; i++)
        table->element_table[i] = NULL;

    free(table);//释放table自身
}

int hash_table_del_cur_element(key_hash_table *table)
{
    if(table == NULL) return 0;

    if (table->curhashelem)
    {
        key_hash_element_t *list_tmp,*list_tmp_next;//用于存放hashtable桶的元素:list_tmp从第一个开始,list_tmp_next用于保存下一个元素(用于纵向列表)
        int index;
        /**
          *在hash桶中重新设置连接
         */
        index = table->curhashelem->hashindex;//获取下标位置
        list_tmp = table->curhashelem->hashprev;//横向的前一个元素
        list_tmp_next = table->curhashelem->hashnext;

        if (list_tmp == 0)
        {
            table->element_table[index] = list_tmp_next;//设置为当前元素的下一个元素
            if (list_tmp_next != 0)//如果存在,则设置前一个元素为空,如果不存在下一个元素,相当于table[index] = 0
                list_tmp_next->hashprev = 0;
        }
        else //
        {
            list_tmp->hashnext = list_tmp_next;
            if (list_tmp_next != 0)
                list_tmp_next->hashprev = list_tmp;
        }

        /**
          *在hash桶中重新设置连接
         */
        list_tmp = table->curhashelem->listprev;
        list_tmp_next = table->curhashelem->listnext;

        if (list_tmp == 0) //要删除的元素为hash桶中为第一个元素
        {
            table->firsthashelem = list_tmp_next; //将要删除元素的下一个元素设置为第一个元素
            if (list_tmp_next != 0)//如果下一个元素存在,设置下一个元素的前一个元素为空
                list_tmp_next->listprev = 0;
            else //要删除的元素同时也是最后一个元素,说明只有一个元素,这时候相当于头和尾都设置为空
                table->lasthashelem = 0;
        }
        else
        {
            list_tmp->listnext = list_tmp_next;
            if (list_tmp_next != 0)
                list_tmp_next->listprev = list_tmp;
            else // table->curhashelem 为列表中的最后一个元素
                table->lasthashelem = list_tmp;
        }
        //删除当前元素所指向的内存
        if(table->curhashelem->b_need_free_value)
        {
            free(table->curhashelem->value);
        }
        free(table->curhashelem);
        table->curhashelem = list_tmp_next; // 设置当前元素指向下一个元素
    }
    else
        return HASH_TABLE_NO_CUR_ELEMENT;
    return 0;
}

void hash_table_custom_free(key_hash_table *table, free_hashtable_func free_func)
{
    if(table == NULL)  return;
    key_hash_element_t *list_tmp = NULL,*list_tmp_next = NULL;//用于存放hashtable桶的元素:list_tmp从第一个开始,list_tmp_next用于保存下一个元素(用于纵向列表)
    /**
    * 释放hashtable所有元素的内存空间
    */
    list_tmp = table->firsthashelem;
    while(list_tmp != 0)//释放元素内存空间
    {
        key_hash_element_t *hash_tmp,*hash_next_tmp; //用于保存横向hashtable的元素,可能多个不同的key,hash值是一样的,这时候就需要在桶中的同一个元素使用链表标识
        hash_tmp = list_tmp->hashnext;//
        while(hash_tmp != 0)
        {
            hash_next_tmp = hash_tmp->hashnext;
            free_func(hash_tmp->value);
            free(hash_tmp);
            hash_tmp = hash_next_tmp;
        }
        list_tmp_next = list_tmp->listnext;//获取hashtbale的下一个元素
        free_func(list_tmp->value);
        free(list_tmp);
        list_tmp = list_tmp_next;
    }
    table->firsthashelem = 0;
    table->lasthashelem = 0;
    table->curhashelem = 0;

    for (int i = 0 ; i < CKEYHASHTABLESIZE ; i++)
        table->element_table[i] = NULL;

    free(table);//释放table自身
}

int hash_table_add_element(key_hash_table* table,const uint32_t key,void *value,int bNeedFreeValue)
{
    if(table == NULL) return HASH_TABLE_NO_EXIST;
    key_hash_element_t *e,*newelem;
    int index = get_hash_table_index(key);//不同的元素可能获取到相同的index
    e = table->element_table[index];
    int found = 0;
    while(!found && e != 0)
    {
        if (e->key == key)
            found = 1;
        else
            e = e->hashnext;
    }
    if(!found)
    {
        //元素key不存在,往hash table中添加新元素
        newelem = (key_hash_element_t*)calloc(1, sizeof(key_hash_element_t));
        newelem->value = value;
        newelem->key = key;
        newelem->hashindex = index;
        newelem->b_need_free_value = bNeedFreeValue;
        if (newelem == 0)
            return HASH_TABLE_OUT_MEMORY;
        e = table->element_table[index];
        table->element_table[index] = newelem;
        newelem->hashnext = e;
        if (e != 0)
            e->hashprev = newelem;
        if (table->firsthashelem == 0)// hash列表中还不存在元素
        {
            table->firsthashelem = newelem;
            table->lasthashelem = newelem;
        }
        else //hash列表中已经存在一些元素
        {
            table->lasthashelem->listnext = newelem;
            newelem->listprev = table->lasthashelem;
            table->lasthashelem = newelem;
        }
    }else
    {

        if (table->cover_old_value)//替换元素
        {
            hash_table_goto_key_element(table, key);
            table->curhashelem->value = value;
        }
        else
        {
            return HASH_TABLE_KEYALREADYEXISTS;
        }
    }
    return 0;
}

int hash_table_goto_key_element(key_hash_table *table,const uint32_t key)
{
    if(table == NULL)
        return HASH_TABLE_NO_EXIST;
    int index = get_hash_table_index(key);//获取下标位置(hash 函数-如何根据key来得到hash的位置)
    table->curhashelem = table->element_table[index];
    int found = 0;
    while(!found && table->curhashelem != 0)
    {
        if (table->curhashelem->key == key)
            found = 1;
        else
            table->curhashelem = table->curhashelem->hashnext;
    }
    if (!found)
        return HASH_TABLE_NO_KEY_ELEMENT;
    return 0;
}

int hash_table_has_key_element(key_hash_table *table, const uint32_t key)
{
    if(table == NULL) return 0;
    key_hash_element_t *tmp;
    int index = get_hash_table_index(key);
    tmp = table->element_table[index];
    int found = 0;
    while(!found && tmp != 0)
    {
        if (tmp->key == key)
            found = 1;
        else
            tmp = tmp->hashnext;
    }
    return found;
}

int hash_table_del_key_element(key_hash_table *table, const uint32_t key)
{
    if(table == NULL) return HASH_TABLE_NO_EXIST;
    int status;
    status = hash_table_goto_key_element(table,key);
    if (status < 0)
        return status;
    return hash_table_del_cur_element(table);
}

void hash_table_goto_first_element(key_hash_table *table)
{
    if(table != NULL)
        table->curhashelem = table->firsthashelem;
}

void hash_table_goto_prev_element(key_hash_table *table)
{
    if(table && table->curhashelem)
        table->curhashelem = table->curhashelem->listprev;
}

void hash_table_goto_last_element(key_hash_table *table)
{
    if(table != NULL)
        table->curhashelem = table->lasthashelem;
}

void hash_table_goto_next_element(key_hash_table *table)
{
    if(table && table->curhashelem)
        table->curhashelem = table->curhashelem->listnext;
}

uint32_t hash_table_get_cur_key(key_hash_table *table)
{
    if(table != NULL && table->curhashelem != NULL)
        return table->curhashelem->key;
    return HASH_TABLE_NO_CUR_KEY;
}

void *hash_table_get_cur_element(key_hash_table *table)
{
    if(table != NULL)
        return  table->curhashelem->value;
    return NULL;
}

int hash_table_has_cur_element(key_hash_table *table)
{
    if(table != NULL)
        return (table->curhashelem == 0)? 0 : 1;
    return 0;
}

调用例子

void test_hashtable()
{
    struct key_hash_table *hashtable = NULL;
    hash_table_new(&hashtable, 1);
    int a = 1;
    int b = 2 ;
    hash_table_add_element(hashtable,1,&a,0);
    hash_table_add_element(hashtable,1,&b,0);
    hash_table_add_element(hashtable,2,&b,0);
    hash_table_add_element(hashtable,3,&a,0);
    hash_table_del_key_element(hashtable,1);
    hash_table_del_key_element(hashtable,3);
    hash_table_del_key_element(hashtable,3);
    hash_table_goto_first_element(hashtable);
    while(hash_table_has_cur_element(hashtable))
    {
        int *c = (int *) hash_table_get_cur_element(hashtable);
        printf("c::%d\n", *c);
        hash_table_goto_next_element(hashtable);
    }
    hash_table_free(hashtable);
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值