• 数据结构与算法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("
    ########## HashTable ##########
    ");
    
        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
    ", *pValue);
    
        pValue = (int *)hashTable_get(&table, "2", NULL);
        if (pValue)
            printf("key 2: %d
    ", *pValue);
    
        pValue = (int *)hashTable_get(&table, "aa", NULL);
        if (pValue)
            printf("key aa: %d
    ", *pValue);
    
        v5 = 50;
        hashTable_set(&table, "aa", &v5);
    
        pValue = (int *)hashTable_get(&table, "aa", NULL);
        if (pValue)
            printf("key aa: %d
    ", *pValue);
    
        hashTable_remove(&table, "aa");
        pValue = (int *)hashTable_get(&table, "aa", NULL);
        if (pValue)
            printf("key aa: %d
    ", *pValue);
    
        hashTable_remove(&table, "1");
        pValue = (int *)hashTable_get(&table, "1", NULL);
        if (pValue)
            printf("key 1: %d
    ", *pValue);
    
        hashTable_free(&table);
    }
    
    int main(int argc, char **argv)
    {
        test_hashTable();
        return 0;
    }

     

  • 相关阅读:
    linux中iptables的用法
    Git介绍及安装配置
    第一个shell脚本
    Nginx配置优化解读
    Python中print格式化输出
    python 程序构架浅析
    Python 常用字符串操作
    Python入门学习:网络刷博器爬虫
    vSphere SDK for Java
    vROPS中获取虚拟机在VC中的UUID
  • 原文地址:https://www.cnblogs.com/chenyuxin/p/15215164.html
Copyright © 2020-2023  润新知