哈希表C语言实现详解

目录

1、数据结构

2、操作函数声明

3、具体实现


1、数据结构

#define HASH_TABLE_MALLOC(size)    rt_malloc(size);
#define HASH_TABLE_REALLOC(p,size) rt_realloc(p,size);
#define HASH_TABLE_CALLOC(n,size)  rt_calloc(n,size);
#define HASH_TABLE_FREE(p)         rt_free(p);

struct hash_table_node;
struct hash_table;

/* 哈希函数,根据key计算哈希值 */
typedef int (*hash_fun)(struct hash_table *table, const void *key);
/* 
 *哈希key比较, key_cmp:传入的要比较的key, key_becmp:哈希表中被比较的key
 * hash桶中的元素是从小到大排列的,
 * 返回值 > 0 : key_cmp > key_becmp
 * 返回值 = 0 : key_cmp = key_becmp
 * 返回值 < 0 : key_cmp < key_becmp
*/
typedef int (*hash_keycmp)(struct hash_table *table, const void *key_cmp, const void *key_becmp);
/* hash桶中的节点数据删除函数,如果插入节点为动态分配,则需要在该函数中释放节点空间 */
typedef int (*node_value_free)(struct hash_table_node *node);

struct hash_table_node
{
    void *key;
    void *value;
    struct hash_table_node *next; /*哈希桶节点下个节点*/
};

struct hash_table
{
    int size; /*哈希桶的大小,即数组的大小*/
    int num;  /*各个哈希桶中节点个数的总和*/
    hash_fun hashfun; /*哈希函数*/
    hash_keycmp keycmp; /*哈希key比较*/
    node_value_free valuefree; /*哈希桶节点数据删除*/
    struct hash_table_node **tables; /*哈希桶,其实就是一个数组*/
};

/*根据当前结构体元素的地址,获取到结构体首地址*/
//#define OFFSETOF(TYPE,MEMBER) ((unsigned int)&((TYPE *)0)->MEMBER)
//#define container(ptr,type,member) ({\
//  const typeof( ((type *)0)->member) *__mptr = (ptr);\
//  (type *) ((char *)__mptr - OFFSETOF(type,member));})

#define OFFSETOF(TYPE,MEMBER) ((unsigned int)&((TYPE *)0)->MEMBER)
#define container(ptr,type,member) ((type *) ((char *)ptr - OFFSETOF(type,member)))

2、操作函数声明

 

extern struct hash_table *hash_table_creat(int size, hash_fun hashfun, hash_keycmp keycmp, node_value_free valuefree);
extern struct hash_table *hash_table_creat_default(int size, node_value_free valuefree);
extern int    hash_table_insert(struct hash_table *hashtable, void *key, void *value);
extern int    hash_table_delete(struct hash_table *hashtable, void *key);
extern int    hash_table_modify(struct hash_table *hashtable, void *key, void *value);
extern void * hash_table_search(struct hash_table *hashtable, void *key);

extern void hash_table_sample(void);

3、具体实现

#include "algo_hash_table.h"
/**
 * 默认使用的哈希函数.
 * 
 * @return 哈希值
 */
static int hash_fun_default(struct hash_table *table, const void *key)
{
    unsigned int hash = 0;
    unsigned int seed = 131;
    char *temp = NULL;
    
    temp = (char*)key;
    while(*temp)
    {
        hash = hash * seed + *temp++;
    }
    
    hash &= 0x7FFFFFFF;
    hash %= table->size;

    return hash;
}

/**
 * 哈希key比较, key_cmp:传入的要比较的key, key_becmp:哈希表中被比较的key
 * 
 * @return > 0 : key_cmp > key_becmp
 * @return = 0 : key_cmp = key_becmp
 * @return < 0 : key_cmp < key_becmp
 * 
 */
static int hash_keycmp_default(struct hash_table *table, const void *key_cmp, const void *key_becmp)
{
    return strcmp(key_cmp, key_becmp);
}

/**
 * 动态创建一个哈希表.
 * 
 * @return NULL:创建失败
 *        !NULL:创建成功
 */
struct hash_table *hash_table_creat(int size, hash_fun hashfun, hash_keycmp keycmp, node_value_free valuefree)
{
    struct hash_table *hashtable = NULL;
    struct hash_table_node **tables = NULL;
    int i = 0;

    if (size < 0 || hashfun == NULL || keycmp == NULL)
        return NULL;

    /*申请哈希表结构空间*/
    hashtable = HASH_TABLE_MALLOC(sizeof(*hashtable));
    if (hashtable == NULL)
        return NULL;
    
    /*申请哈希桶数据空间,实际就是申请size大小的数组空间*/
    tables = (struct hash_table_node**)HASH_TABLE_MALLOC(size * sizeof(tables));
    if (tables == NULL)
        return NULL;

    hashtable->num       = 0;
    hashtable->size      = size;
    hashtable->tables    = tables;
    hashtable->hashfun   = hashfun;
    hashtable->keycmp    = keycmp;
    hashtable->valuefree = valuefree;

    /*清零哈希桶(数组)空间*/
    for (i = 0; i < size; i++)
    {
        hashtable->tables[i] = NULL;
    }
    
    return hashtable;
}

/**
 * 使用默认的函数函数、key比较函数、节点删除函数 动态创建一个哈希表.
 * 
 * @return NULL:创建失败
 *        !NULL:创建成功
 */
struct hash_table *hash_table_creat_default(int size, node_value_free valuefree)
{
    return hash_table_creat(size, hash_fun_default, hash_keycmp_default, valuefree);
}

/**
 * 向一个哈希桶插入一个节点,有3种情况:
 * 1、prev==NULL,插入位置是头结点 2、key小于cur->key 3、cur==NULL,链表尾插入
 * 
 * @param hashtable: 散列表
 * @param key: 关键值
 * @param value: 节点数据
 * @param value: 节点数据大小
 * 
 * @return 0:插入成功
 *        -1:哈希表不存在 或 key为空 或 value为空
 *        -2:节点已经存在
 */
int hash_table_insert(struct hash_table *hashtable, void *key, void *value)
{
    struct hash_table_node *prev = NULL;
    struct hash_table_node *cur = NULL;
    struct hash_table_node *new_node = NULL;
    int hvalue = 0;

    if (hashtable == NULL || key == NULL || value == NULL)
        return -1;

    /*根据key计算出哈希值*/
    hvalue = hashtable->hashfun(hashtable, key);
    cur = hashtable->tables[hvalue];

    /*hash桶中的元素是从小到大排列的,查找要插入的位置*/
    while((cur != NULL) && (hashtable->keycmp(hashtable, key, cur->key) > 0))
    {
        prev = cur;
        cur = cur->next;
    }

    /*如果key相同,表示节点以及存在,直接返回*/
    if ((cur != NULL) && (hashtable->keycmp(hashtable, key, cur->key) == 0))
        return -2;

    /*插入新增节点*/
    new_node = (struct hash_table_node*)HASH_TABLE_MALLOC(sizeof(*new_node));
    if (new_node == NULL)
        return NULL;

    new_node->key = key;
    new_node->value = value;
    if (prev == NULL) /*第1种情况*/
    {
        new_node->next = NULL;
        hashtable->tables[hvalue] = new_node;/*这里不能为cur = new_node,因为此时的cur和table[hvalue]都是无效的*/
    }
    else /*第2、3种情况*/
    {
        new_node->next = cur;
        prev->next = new_node;
    }
    
    hashtable->num ++;

    return 0;
}


/**
 * 删除一个节点.
 * 
 * @param hashtable: 散列表
 * @param key: 删除节点关键值
 * 
 * @return 0:删除成功
 *        -1:哈希表不存在 或 key为空
 *        -2:节点不存在
 */
int hash_table_delete(struct hash_table *hashtable, void *key)
{
    struct hash_table_node *prev = NULL;
    struct hash_table_node *cur = NULL;
    int hvalue = 0;

    if (hashtable == NULL || key == NULL)
        return -1;

    /*根据key计算出哈希值*/
    hvalue = hashtable->hashfun(hashtable, key);
    cur = hashtable->tables[hvalue];

    /*hash桶中的元素是从小到大排列的,查找要删除的位置*/
    while((cur != NULL) && (hashtable->keycmp(hashtable, key, cur->key) >= 0))
    {
        if (hashtable->keycmp(hashtable, key, cur->key) == 0)
        {
            if (prev == NULL)/*如果删除的是头结点*/
            {
                hashtable->tables[hvalue] = cur->next;
            }
            else
            {
                prev->next = cur->next;
            }
            /*若节点所指向的数据(包括key和value)为动态分配,则需要在这里释放*/
            hashtable->valuefree(cur);
            HASH_TABLE_FREE(cur);

            hashtable->num --;
            return 0;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }

    return -2;
}

/**
 * 修改一个节点.注意事项:
 * 1、会先释放节点指向的就数据空间(这里如果是realloc更大的数据空间,容易造成指针泄露,且是不知道整个数据结构的大小的) 
 * 2、修改的节点必须为新动态分配的空间
 *
 * @param hashtable: 散列表
 * @param key: 修改节点关键值
 * @param value: 修改节点数据
 * 
 * @return 0:修改成功
 *        -1:哈希表不存在 或 key为空 或value为空
 *        -2:节点不存在
 */
int hash_table_modify(struct hash_table *hashtable, void *key, void *value)
{
    struct hash_table_node *cur = NULL;
    int hvalue = 0;

    if (hashtable == NULL || key == NULL || value == NULL)
        return -1;

    /*根据key计算出哈希值*/
    hvalue = hashtable->hashfun(hashtable, key);
    cur = hashtable->tables[hvalue];

    /*hash桶中的元素是从小到大排列的,查找要修改的位置*/
    while((cur != NULL) && (hashtable->keycmp(hashtable, key, cur->key) >= 0))
    {
        if (hashtable->keycmp(hashtable, key, cur->key) == 0)
        {
            hashtable->valuefree(cur);
            cur->key = key;
            cur->value = value;
            return 0;
        }
        else
        {
            cur = cur->next;
        }
    }

    return -2;
}

/**
 * 根据key查找节点数据.
 * 
 * @param hashtable: 散列表
 * @param key: 查找节点关键值
 * @param value: 查找节点数据
 * 
 * @return NULL:查找失败
 *        !NULL:查找成功
 */
void * hash_table_search(struct hash_table *hashtable, void *key)
{
    struct hash_table_node *cur = NULL;
    int hvalue = 0;

    if (hashtable == NULL || key == NULL)
        return NULL;

    /*根据key计算出哈希值*/
    hvalue = hashtable->hashfun(hashtable, key);
    cur = hashtable->tables[hvalue];

    /*hash桶中的元素是从小到大排列的,查找要查找的位置*/
    while((cur != NULL) && (hashtable->keycmp(hashtable, key, cur->key) >= 0))
    {
        if (hashtable->keycmp(hashtable, key, cur->key) == 0)
        {
            return cur->value;
        }
        else
        {
            cur = cur->next;
        }
    }

    return NULL;
}


/*******************************************************************************************
 *                                          使用示例
 *******************************************************************************************/
struct test_node
{
    char key[10];
    char value[10];
};

int node_value_free_sample(struct hash_table_node *node)
{
    struct test_node *node_temp = NULL;

    /*根据key在test_node结构体中的偏移地址,找到散列表节点实际指向的结构体首地址*/
    node_temp = container(node->key, struct test_node, key);
    /*如果节点所指向数据空间为动态申请的则需要释放*/
    HASH_TABLE_FREE(node_temp);
    
    return 0;
}

struct hash_table *hash_table_test;
char table_node_read[5][10];

void hash_table_sample(void)
{
    int i = 0;
    struct test_node *node_temp = NULL;
    char rd_key[10] = {10}, del_key[10] = {0};
    char *temp = NULL;
    
    hash_table_test = hash_table_creat_default(5, node_value_free_sample);
    
    /*插入 -- 查询*/
    for (i=0; i<5; i++)
    {
        node_temp = HASH_TABLE_MALLOC(sizeof(*node_temp));
        memset(node_temp, 0, sizeof(*node_temp));
        sprintf(node_temp->key, "AAA%d", i);
        sprintf(node_temp->value, "%d", i+10);
        hash_table_insert(hash_table_test, node_temp->key, node_temp->value);
    }
    for (i=0; i<5; i++)
    {
        sprintf(rd_key, "AAA%d", i);
        temp = hash_table_search(hash_table_test, rd_key);
        memcpy(table_node_read[i], temp, 10);
    }
    
    /*修改 -- 查询*/
    for (i=0; i<5; i++)
    {
        node_temp = HASH_TABLE_MALLOC(sizeof(*node_temp));
        memset(node_temp, 0, sizeof(*node_temp));
        sprintf(node_temp->key, "AAA%d", i);
        sprintf(node_temp->value, "%d", i+20);
        hash_table_modify(hash_table_test, node_temp->key, node_temp->value);
    }
    for (i=0; i<5; i++)
    {
        sprintf(rd_key, "AAA%d", i);
        temp = hash_table_search(hash_table_test, rd_key);
        memcpy(table_node_read[i], temp, 10);
    }
    
    /*删除 -- 查询*/
    for (i=0; i<3; i++)
    {
        sprintf(del_key, "AAA%d", i);
        hash_table_delete(hash_table_test, del_key);
    }
    for (i=0; i<5; i++)
    {
        temp = NULL;
        memset(table_node_read[i], 0, 10);
        sprintf(rd_key, "AAA%d", i);
        temp = hash_table_search(hash_table_test, rd_key);
        if (temp != NULL)
        {
            memcpy(table_node_read[i], temp, 10);
        }
    }
}

 

 

你可能感兴趣的:(数据结构与算法)