数据结构与算法5 — 哈希表

尊重作者劳动成果,转载请注明出处,谢谢!

1. hash.h

#ifndef hash_H
#define hash_H

#include <stddef.h>
#include <sys/types.h>

//哈希表节点,链表结构
typedef struct hashNode
{
    char *key;             //
    void *value;           //
    struct hashNode *next; //下一个节点
} HashNode;

#ifdef __cplusplus
extern "C"
{
#endif
    size_t hashTable_hash31(const char *key, size_t n);
    size_t hashTable_hash33(const char *key, size_t n);
    HashNode *hashTable_createNode(const char *key, const void *value, size_t valueSize);
    void hash_freeNode(HashNode *node);
#ifdef __cplusplus
}
#endif

#endif

2. hash.c

#include "hash.h"
#include <stdlib.h>
#include <string.h>

//计算hash值,即计算key值对应哈希表的索引(结果范围为[0,n-1])
size_t hashTable_hash31(const char *key, size_t n)
{
    register size_t h;
    register unsigned char *p = (unsigned char *)key;

    for (h = 0; *p; p++)
    {
        h = h * 31 + *p;
    }

    return h % n; //模运算,取值为[0,n-1]
}

//计算hash值,即计算key值对应哈希表的索引(结果范围为[0,n-1])
size_t hashTable_hash33(const char *key, size_t n)
{
    register size_t h;
    register unsigned char *p = (unsigned char *)key;

    for (h = 0; *p; p++)
    {
        h = h * 33 + *p;
    }

    return h % n;
}

//创建哈希表节点
HashNode *hashTable_createNode(const char *key, const void *value, size_t valueSize)
{
    HashNode *node = (HashNode *)malloc(sizeof(HashNode));
    if (node == NULL)
        return NULL;

    //初始化以及赋值,这里的键和值都需要进行内存的分配,以达到深拷贝的目的
    memset(node, 0, sizeof(HashNode));
    node->key = (char *)malloc(strlen(key) + 1);
    if (node->key == NULL)
    {
        hash_freeNode(node);
        return NULL;
    }

    node->value = malloc(valueSize);
    if (node->value == NULL)
    {
        hash_freeNode(node);
        return NULL;
    }

    strcpy(node->key, key);
    memcpy(node->value, value, valueSize);
    return node;
}

//释放哈希表节点
void hash_freeNode(HashNode *node)
{
    if (node == NULL)
        return;

    if (node->key != NULL)
        free(node->key);

    if (node->value != NULL)
        free(node->value);

    node->next = NULL;
    free(node);
}

3. hashTable.h

#ifndef hashTable_H
#define hashTable_H

#include <stddef.h>
#include "hash.h"

//哈希表,采用数组加链表(拉链法)的实现方式
typedef struct
{
    HashNode **hashSet; //指针数组,对应每个链表的头指针
    size_t n;           //数组长度,即哈希表里的链表数
    size_t valueSize;   //值的大小(字节)
} HashTable;

//定义该宏可以直观的看出哈希表元素的数据类型,比如:HashTable(int)
#define HashTable(type) HashTable

#ifdef __cplusplus
extern "C"
{
#endif
    int hashTable_init(HashTable *table, size_t valueSize, size_t n);
    void hashTable_free(HashTable *table);
    void hashTable_clear(HashTable *table);
    int hashTable_insert(HashTable *table, const char *key, const void *value);
    int hashTable_remove(HashTable *table, const char *key);
    int hashTable_set(HashTable *table, const char *key, const void *value);
    void *hashTable_get(HashTable *table, const char *key, void *data);
#ifdef __cplusplus
}
#endif

#endif

4. hashTable.c

#include "hashTable.h"
#include <string.h>
#include <stdlib.h>

//初始化哈希表
int hashTable_init(HashTable *table, size_t valueSize, size_t n)
{
    if (table == NULL || n <= 0)
        return -1;

    memset(table, 0, sizeof(HashTable));
    table->hashSet = (HashNode **)malloc(n * sizeof(HashNode *)); //指针数组,注意元素大小为:sizeof(HashNode *)
    table->valueSize = valueSize;
    table->n = n;
    return 0;
}

//释放哈希表
void hashTable_free(HashTable *table)
{
    if (table == NULL)
        return;

    hashTable_clear(table);
    if (table->hashSet != NULL)
    {
        free(table->hashSet);
        table->hashSet = NULL;
    }
    table->n = 0;
    table->valueSize = 0;
}

//清空哈希表
void hashTable_clear(HashTable *table)
{
    if (table == NULL)
        return;

    size_t i;
    HashNode *node;
    for (i = 0; i < table->n; i++)
    {
        //从头到尾进行删除
        while (table->hashSet[i] != NULL)
        {
            node = table->hashSet[i];
            table->hashSet[i] = node->next;
            hash_freeNode(node); //释放节点内存
        }
    }
}

//插入数据,不管key值是否存在
int hashTable_insert(HashTable *table, const char *key, const void *value)
{
    if (table == NULL || key == NULL)
        return -1;

    HashNode *node = hashTable_createNode(key, value, table->valueSize);
    if (node == NULL)
        return -1;

    size_t index = hashTable_hash31(key, table->n); //计算key值对应的索引
    HashNode *header = table->hashSet[index];       //取到对应的链表头指针
    node->next = header;                            //头插法
    table->hashSet[index] = node;
    return 0;
}

//删除数据,重复的key值也将删除
int hashTable_remove(HashTable *table, const char *key)
{
    if (table == NULL || key == NULL)
        return -1;

    size_t index = hashTable_hash31(key, table->n);  //计算key值对应的索引
    HashNode **ptrHeader = &(table->hashSet[index]); //链表头指针的指针

    while (*ptrHeader != NULL)
    {
        if (strcmp((*ptrHeader)->key, key) == 0)
        {
            HashNode *node = *ptrHeader;     //被删除节点的指针
            *ptrHeader = (*ptrHeader)->next; //指向下一个指针,修改的是ptrHeader指针所指向的内容,这里需要好好理解
            hash_freeNode(node);             //释放被删除节点的内存
        }
        else
        {
            ptrHeader = &(*ptrHeader)->next; //下一个节点的指针
        }
    }

    return 0;
}

//修改或插入数据
int hashTable_set(HashTable *table, const char *key, const void *value)
{
    if (table == NULL || key == NULL)
        return -1;

    size_t index = hashTable_hash31(key, table->n); //计算key值对应的索引
    HashNode *header = table->hashSet[index];
    while (header != NULL)
    {
        //key值已经存在
        if (strcmp(header->key, key) == 0)
        {
            memcpy(header->value, value, table->valueSize);
            return 0;
        }

        header = header->next; //下一个节点的指针
    }

    //key值不存在,采用头插法插入数据
    HashNode *node = hashTable_createNode(key, value, table->valueSize);
    if (node == NULL)
        return -1;

    header = table->hashSet[index]; //取到对应的链表
    node->next = header;            //头插法
    table->hashSet[index] = node;
    return 0;
}

//查找数据,成功返回数据的指针,并且当data参数不为空时,将数据写入data参数,失败返回NULL
void *hashTable_get(HashTable *table, const char *key, void *data)
{
    if (table == NULL || key == NULL)
        return NULL;

    size_t index = hashTable_hash31(key, table->n); //计算key值对应的索引
    HashNode *header = table->hashSet[index];

    while (header != NULL)
    {
        if (strcmp(header->key, key) == 0)
        {
            if (data != NULL)
                memcpy(data, header->value, table->valueSize);

            return header->value;
        }

        header = header->next;
    }

    return NULL;
}

5. main.c

#include "hashTable.h"
#include "concurrentHashTable.h"
#include <stdio.h>

void test_hashTable()
{
    printf("\n########## HashTable ##########\n");

    HashTable(int) table;
    hashTable_init(&table, sizeof(int), 10);

    int v1 = 1;
    int v2 = 2;
    int v3 = 3;
    int v4 = 4;
    int v5 = 5;
    int v6 = 6;
    int v7 = 7;

    hashTable_insert(&table, "1", &v1);
    hashTable_insert(&table, "1", &v2);
    hashTable_insert(&table, "1", &v3);
    hashTable_insert(&table, "2", &v4);
    hashTable_insert(&table, "aa", &v5);
    hashTable_insert(&table, "bb", &v6);
    hashTable_insert(&table, "cc", &v7);

    int *pValue;
    //因为使用的是头插法,所以获取到最后插入的值 3
    pValue = (int *)hashTable_get(&table, "1", NULL);
    if (pValue)
        printf("key 1: %d\n", *pValue);

    pValue = (int *)hashTable_get(&table, "2", NULL);
    if (pValue)
        printf("key 2: %d\n", *pValue);

    pValue = (int *)hashTable_get(&table, "aa", NULL);
    if (pValue)
        printf("key aa: %d\n", *pValue);

    v5 = 50;
    hashTable_set(&table, "aa", &v5);

    pValue = (int *)hashTable_get(&table, "aa", NULL);
    if (pValue)
        printf("key aa: %d\n", *pValue);

    hashTable_remove(&table, "aa");
    pValue = (int *)hashTable_get(&table, "aa", NULL);
    if (pValue)
        printf("key aa: %d\n", *pValue);

    hashTable_remove(&table, "1");
    pValue = (int *)hashTable_get(&table, "1", NULL);
    if (pValue)
        printf("key 1: %d\n", *pValue);

    hashTable_free(&table);
}

int main(int argc, char **argv)
{
    test_hashTable();
    return 0;
}

 

posted @ 2021-09-01 18:16  chenyuxin  阅读(97)  评论(0编辑  收藏  举报