一种用C实现的hashmap

本文介绍了一位开发者使用C语言自定义的哈希表实现,包括数组+链表的数据结构,未实现缩容功能。作者分享了关键代码片段,并探讨了其优缺点,期待读者提出改进建议。

前言      

        对c简介的语法一直念念不忘;当然它还是redis、pgsql、Linux的实现语言,当然我没看过任何一个的源码;还有仍记得大学时和小美一起讨论c语言,她那渐渐升起红晕的脸....

        用了一段时间之后感觉c类库真的不太多,也可能找错了方向。

简单介绍

        用数组加链表的方式实现了一个简单的hashmap,没有实现缩容的部分。        

        除了静态方法均已mymap开头,这样命名是怕找打了更好的实现和别人的方法名起冲突。

        当map中存在的元素超出MAX_CAPACITY后,或者map的数组长度超出MAX_ARR_LEN就不在扩容。刷一般的算法基本够用了。

        由于个人能力的问题,实现方式肯定会存在不合理的地方,存取的效率可能也比较低下,希望您能指出。

        

        

代码

my_map.h

#pragma once

#define MAX_CAPACITY    256    // pow of 2
#define MAX_ARR_LEN     32
#define MAX_LIST_LEN    8      //
#define INIT_ARR_LEN    8
#define LOAD_FACTOR     0.75

typedef struct MapNodeStruct
{
    int list_len;

    void *key;

    void *value;

    struct MapNodeStruct *next;  
} MapNodeStruct, *MapNode;

// arr + list
typedef struct MapStruct
{
    //容量
    int capacity;

    //数组大小
    int arr_len;

    //compare 0 eq !0 ne
    int (*compare)(void *key, void *search);

    MapNode node_arr;

    //如需要根据特定的条件在map中查找数据
    int (*hashcode)(void *key);
} MapStruct, *Map ;

typedef void *MyMap;


static int mymap_hash_code(void *key);

static int mymap_get_index(Map mapp, void *key);

static MapNode mymap_delete_list(MapNode head , MapNode be_del);

// 对应mymap_free_map
static MapNode mymap_free_list(MapNode head);

// 对应mymap_free_atomic
static MapNode mymap_free_list_atomic(MapNode head);

static MapNode mymap_resize(Map mapp);

static void mymap_insert_list(MapNode header , MapNode be_insert);

//初始化方法,hashcode为内存地址,
MyMap mymap_init(int compare(void *key , void *search));

MyMap mymap_init_hash(int hashcode(void *key) , int compare(void *key , void *search));

// 只释放map本身,map对应的key 和 value不释放
MyMap mymap_free(MyMap map);

// 释放map本身,释放map对应的key 和 value
MyMap mymap_free_atomic(MyMap map);

void *mymap_put(MyMap , void *key , void *value);

void *mymap_get(MyMap , void *key);

void *mymap_delete(MyMap , void *key);

int mymap_size(MyMap);

//callback 0 continue , 1 break
void mymap_foreach(MyMap , int callback(void *key, void *value));

my_map.c

#include "my_map.h"
#include <stdlib.h>


static int mymap_hash_code(void *key)
{
    int h = *(int*)key;
    h ^= (h >> 20) ^ (h >> 12);
    return h ^ (h >> 7) ^ (h >> 4);
}

static int mymap_get_index(Map map, void *key)
{
    int hashcode = map->hashcode(key);
    //int index = hashcode ^ (map->arr_len - 1);
    int index = hashcode & (map->arr_len - 1);
    return index;
}

/**
 * @brief compare 为0时相等
 * 
 * @param compare 
 * @return MyMap 
 */
MyMap mymap_init(int compare(void *key , void *search))
{

    Map map = calloc(1 , sizeof(struct MapStruct));
    if(!map)
    {
        return NULL;
    }
    map->compare = compare;

    map->arr_len = INIT_ARR_LEN;
    map->node_arr = calloc(INIT_ARR_LEN , sizeof(struct MapNodeStruct));

    if(!map->node_arr)
    {
        return NULL;
    }

    map->hashcode = mymap_hash_code;

    return map;
}

MyMap mymap_init_hash(int hashcode(void *key) , int compare(void *key , void *search))
{
    if(hashcode == NULL)
        return NULL;

    Map map = mymap_init(compare);
    if(map == NULL)
        return NULL;

    map->hashcode = hashcode;

    return map;
}

void *mymap_put(MyMap myMap, void *key , void *value)
{
    if(key && myMap)
    {
        Map map = myMap;

        //pre 最后一个节点,不为 NULL
        MapNode headp = NULL , currp = NULL , pre = NULL;
        //int list_len = 0;

        int index = mymap_get_index(map, key);

        if((headp = (map->node_arr + index)) && headp->key)
        {
            currp = headp;
            while(currp)
            {
                if(key == currp->key || !map->compare(key , currp->key))
                {
                    void *old_value = currp->value;
                    currp->value = value;
                    return old_value;
                }
                pre = currp;
                currp = currp->next;
                //++ list_len;
            }

            pre->next = calloc(1 , sizeof(struct MapNodeStruct));
            if(!pre->next)
                return (void*)-1;

            pre->next->key = key;
            pre->next->value = value;

            ++ headp->list_len;
            ++ map->capacity;

            if (headp->list_len >= MAX_LIST_LEN)
            {
                //
                mymap_resize(myMap);
            }
            
        }
        else
        {
            headp->key = key;
            headp->value = value;
            headp->list_len = 1;

            ++ map->capacity;
        }

        if(map->arr_len * LOAD_FACTOR < map->capacity)
            mymap_resize(myMap);
    }
    return NULL;
}

void *mymap_get(MyMap myMap, void *key)
{
    if(key && myMap)
    {
        Map map = myMap;
        int index = mymap_get_index(map, key);

        MapNode node = NULL;
        if(node = (map->node_arr + index))
        {
            while(node && node->key)
            {
                if(key == node->key || !map->compare(key , node->key))
                {
                    return node->value;
                }
                node = node->next;
            }
        }
    }
    return NULL;
}

void *mymap_delete(MyMap myMap, void *key)
{
    if(key && myMap)
    {
        Map map = myMap;
        MapNode node = NULL , be_del = NULL;
        void *old_value;

        int index = mymap_get_index(map, key);

        if(node = (map->node_arr + index))
        {
            be_del = node;
            while(be_del && be_del->key)
            {
                if(key == be_del->key || !map->compare(key , be_del->key))
                {
                    node = mymap_delete_list(node , be_del);
                    -- map->capacity;
                    old_value = be_del->value;
                    //删除后将新链表挂在数组上
                    // 结构体直接赋�? �? 出现表达式必须是可修改的左�?
                    //(map->node_arr + index) = node;

                    if(node)
                    {
                        (map->node_arr + index)->key = node->key;
                        (map->node_arr + index)->value = node->value;
                        (map->node_arr + index)->next = node->next;
                    }
                    else
                    {
                        (map->node_arr + index)->key = NULL;
                        (map->node_arr + index)->value = NULL;
                        (map->node_arr + index)->next = NULL;
                    }

                    return old_value;
                }
                be_del = be_del->next;
            }
        }
    }
    return NULL;
}

static MapNode mymap_delete_list(MapNode head , MapNode be_del)
{
    MapNode pre = head , curr = head;

    while(curr != be_del)
    {
        pre = curr;
        curr = curr->next;
    }

    //删除节点是头节点
    if(pre == curr)
    {
        head = curr->next;
        if(head)
            head->list_len = head->list_len - 1;
    }
    else
    {
        pre->next = be_del->next;
        head->list_len = head->list_len - 1;
        free(be_del);
    }
    return head;
}

static void mymap_insert_list(MapNode header , MapNode be_insert)
{
    while(header && header->next)
    {
        header = header->next;
    }
    header->next = be_insert;
}


static MapNode mymap_free_list(MapNode head)
{
    MapNode temp = NULL;
    //头结点属于node_arr 数组的一部分,不在这里释放
    while(head && (temp = head->next))
    {
        head = temp->next;
        free(temp);
    }
    return NULL;
}

static MapNode mymap_free_list_atomic(MapNode head)
{
    MapNode temp = NULL;
    //头结点属于node_arr 数组的一部分,不在这里释放
    while(head && (temp = head->next))
    {
        if(temp->key != temp->value)
        {
            free(temp->key);
            free(temp->value);
        }
        else
        {
            free(temp->key);
        }
        head = temp->next;
        free(temp);
    }
    return NULL;
}

MyMap mymap_free(MyMap myMap)
{
    if(myMap)
    {
        Map map = myMap;
        for(int i = 0; i < map->arr_len ; ++ i)
        {
            mymap_free_list(map->node_arr + i);
        }

        free(map->node_arr);
        
        map->node_arr = NULL;
        //arr
        free(map);

        map = NULL;
    }
    return NULL;
}

static MapNode mymap_resize(Map map)
{
    if(map->capacity >= MAX_CAPACITY || map->arr_len == MAX_ARR_LEN)
        return map->node_arr;

    MapNode newnodep = calloc(map->arr_len << 1, sizeof(struct MapNodeStruct));
    if(!newnodep)
        return map->node_arr;

    int new_index = 0 , new_list_len = 0 , old_list_len = 0;
    MapNode temp_nodep = NULL , new_headp = NULL,  old_headp = NULL;

    for(int i = 0; i < map->arr_len; ++ i)
    {
        temp_nodep = map->node_arr + i;

        //数组上的节点应该不为空,通过key判断该位置有没有元素
        while(temp_nodep && temp_nodep->key)
        {
            //hashcode  1101 & 0111 = 0101
            //          1101 & 1111 = 1101
            //          1101 & 1000 = 1000
            //          1000 & 0111 = 0000
            //          1000 & 1000 = 1000
            // 条件成立加入new_headp ,不成立加入old_headp
            // new_headp = old_headp + arr_len
            if(map->arr_len & mymap_hash_code(temp_nodep->key))
            {
                if(!new_headp)
                {
                    new_headp = (newnodep + i + map->arr_len);

                    new_headp->key = temp_nodep->key;
                    new_headp->value = temp_nodep->value;
                    //new_headp ->next = temp_nodep->next;
                    new_headp->list_len = 0;
                }
                else
                {
                    mymap_insert_list(new_headp , temp_nodep);
                }
                ++ new_headp->list_len;
            }
            else
            {
                if(!old_headp)
                {
                    old_headp = (newnodep + i);

                    old_headp->key = temp_nodep->key;
                    old_headp->value = temp_nodep->value;
                    //old_headp->next = temp_nodep->next;
                    old_headp->list_len = 0;
                }
                else
                {
                    mymap_insert_list(old_headp , temp_nodep);
                }
                ++ old_headp->list_len;
            }

            temp_nodep = temp_nodep->next;
        }
        new_headp = NULL,  old_headp = NULL;
    }

    free(map->node_arr);


    map->node_arr = newnodep;
    map->arr_len = map->arr_len << 1;
    return newnodep;
}

MyMap mymap_free_atomic(MyMap myMap)
{
    if(myMap)
    {
        Map map = myMap;
        for(int i = 0; i < map->arr_len ; ++ i)
        {
            mymap_free_list_atomic(map->node_arr + i);
        }

        free(map->node_arr);
        
        map->node_arr = NULL;
        //arr
        free(map);

        map = NULL;
    }
    return NULL;
}

int mymap_size(MyMap myMap)
{
    if(myMap)
    {
        int size = 0;
        Map map = myMap;
        return map->capacity;
        /*
        for(int i = 0; i < map->arr_len ; ++ i)
        {
            size += (map->node_arr + i)->list_len;
        }
        return size;
        */
    }
    return 0;
}

void mymap_foreach(MyMap myMap , int callback(void *key, void *value))
{
    if(myMap)
    {
        Map map = myMap;
        MapNode node = NULL , temp = NULL;
        for(int i = 0; i < map->arr_len ; ++ i)
        {
            node = (map->node_arr + i);
            //printf("\n%d,%d\n" , i, node->list_len);
            while(node && node->key)
            {
                temp = node->next;
                if(callback(node->key , node->value))
                {
                    break;
                }
                node = temp;
            }
            
        }
    }
}

基本测试

#include <stdio.h>
#include <string.h>
#include "my_map.h"
#include "my_map.c"

typedef struct student_struct
{
    int score;
    char *name;
    char *grade;
} strudent;

/*
void test_map_node()
{
    my_nodep nodep0 = calloc(1 , sizeof(struct my_map_node));
    my_nodep nodep1 = calloc(1 , sizeof(struct my_map_node));
    my_nodep nodep2 = calloc(1 , sizeof(struct my_map_node));
    my_nodep nodep3 = calloc(1 , sizeof(struct my_map_node));


    nodep0->value = (void*)0;
    //nodep0->next = nodep1;

    (nodep1)->value = (void*)1;
    (nodep1)->next = nodep2;

    (nodep2)->value = (void*)2;
    (nodep2)->next = nodep3;

    (nodep3)->value = (void*)3;

    my_nodep temp = mymap_delete_list(nodep0 , nodep0);
    printf("%d\n" , temp->value);
}
*/

int compare(void *key , void *search)
{
    int i = *(int *)key;
    int j = *(int *)search;

    return i-j;
}

int callback(void *key , void *value)
{
    //printf("key = %d,value = %d\n" , key , value);
    return 0;
}

int compare_struct(void *key , void *search)
{
    strudent *keyp = key , *searchp = search;
    return strcmp(keyp->name , searchp->name);
}

int compare_struct2(void *key , void *search)
{
    char *keyp = key ;
    
    char *searchp = search;
    return strcmp(keyp , searchp);
}

int hashcode(void *key)
{
    char *keyp = key;

    return atoi(keyp) * 5 + 1;
}

void test_map_basic_struct()
{
    strudent *studs = calloc(1 , sizeof(strudent));

    studs->score = 99;
    studs->name = "测试test";

    MyMap mymap = mymap_init(compare_struct);
    mymap_put(mymap , studs , studs );
   
    void *value = mymap_get(mymap , studs);
    printf("%s\n" , ((strudent*)value)->name);

    value = mymap_get(mymap , studs);
    printf("%s\n" , ((strudent*)value)->name);

    value = mymap_delete(mymap ,studs);
    printf("%s\n" , ((strudent*)value)->name);

    value = mymap_get(mymap , studs);
    printf("%s\n" , value ? ((strudent*)value)->name : "null");

    //free(studs);
    mymap_free(mymap);

    mymap = mymap_init_hash(hashcode , compare_struct2);

    char *name1 = calloc(16, sizeof(char));
    memcpy(name1 , "hellomap" , 9);

    char *name2 = calloc(16, sizeof(char));
    memcpy(name2 , "hellomap" , 9);

    mymap_put(mymap , name1 , studs);

    value = mymap_get(mymap , name2);
    printf("%s\n" , value ? ((strudent*)value)->name : "null");

    mymap_free(mymap);
    free(studs);

}

void test_free(void)
{
    int *int_arr = malloc(10 * sizeof(int));

    free(int_arr + 9);

    free(int_arr + 8);

    free(int_arr);
}

void test_map_basic()
{
    MyMap mymap = mymap_init(compare);
    mymap_put(mymap , 1 , 1);
    mymap_put(mymap , 2 , 8);
    mymap_put(mymap , 1 , 3);
    mymap_put(mymap , 1 , 4);

    void *value = mymap_get(mymap , 1);
    printf("%d\n" , (int)value);

    value = mymap_get(mymap , 2);
    printf("%d\n" , (int)value);

    value = mymap_delete(mymap , 2);
    printf("%d\n" , (int)value);

    value = mymap_get(mymap , 2);
    printf("%d\n" , (int)value);
}

void test_map_batch()
{
    MyMap mymap = mymap_init(compare);
    
    printf("--------------put---------------\n");
    for(int i = 1 ; i <= 512 ; ++ i)
    {
        mymap_put(mymap , i , i * 2);
    }

    printf("\n--------------get---------------\n");
    void *value = NULL;
    for(int i = 1 ; i <= 512 ; ++ i)
    {
         value = mymap_get(mymap , i);
         if(value)
         {
            printf("%d\t" , value);
         }
    }
    printf("\n--------------foreach---------------\n");
    mymap_foreach(mymap , callback);

    printf("\n--------------size---------------\n");
    int size = mymap_size(mymap);
    printf("%d\t" , size);


    printf("\n--------------del---------------\n");
    for(int i = 1 ; i <= 10 ; ++ i)
    {
         value = mymap_delete(mymap , i);
         if(i << 1 != (int)value)
         {
            printf("%d\t" , (int)value);
         }
    }
    printf("\n--------------get---------------\n");
    for(int i = 1 ; i <= 10 ; ++ i)
    {
         value = mymap_get(mymap , i);
         if(value)
            printf("%d\t" , (int)value);
    }
    printf("\n--------------end---------------\n");

    mymap_free(mymap);
}

int main(int argc , char *argv[])
{
    test_map_basic_struct();

    //test_free();
    return EXIT_SUCCESS;
}

来个测试截图

求一棵二叉树的宽度

/**
 如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)
 */

#include "../../util/my_util.h"
#include "../../util/my_util.c"




typedef struct NodeStruct 
{
    int value;
    struct NodeStruct *left;
    struct NodeStruct *right;
} 
NodeStruct, *Node;

typedef struct NodeStruct *Node;

int compare(void *key , void *search)
{
    return ((Node)key)->value - ((Node)search)->value;
}

int get_max_width(Node head)
{
    if(!head)
        return 0;
    
    int max_width = 0;
    int cur_width = 0;
    int cur_level = 0;

    MyMap level_map =  mymap_init(compare);    
    mymap_put(level_map , head, 1);

    MyList queue = mylist_init();
    mylist_insert_head(queue , head);

    Node node = NULL;
    Node left = NULL;
    Node right = NULL;

    int curlevel = 0;

    while(mylist_size(queue))
    {
        node = mylist_poll_tail(queue);
        left = node->left;
        right = node->right;

        curlevel = mymap_get(level_map , node);
        if(left)
        {
            mymap_put(level_map , left , curlevel + 1);
            mylist_insert_head(queue ,left);
        }

        if(right)
        {
            mymap_put(level_map , right , curlevel + 1);
            mylist_insert_head(queue ,right);
        }

        if(curlevel > cur_level)
        {
            /*
                此处为0 , 感觉不对
                如根节点或者每一层的最左边的节点进入,宽度需要被设置成1
            */
            cur_width = 1;
            cur_level = curlevel;
            max_width = MAX(max_width,cur_width);
        }
        else
        {
            ++ cur_width;
        }

        max_width = MAX(max_width,cur_width);
    }

    mylist_free(queue);
    mymap_free(level_map);

    return max_width;
}

int main(void)
{
    Node tree = calloc(8 , sizeof(struct NodeStruct));

    tree->left = tree + 1;
    tree->right = tree + 2;

    (tree + 1)->left = tree + 3;
    (tree + 1)->right = tree + 4;

    (tree + 2)->left = tree + 5;
    (tree + 2)->right = tree + 6;

    (tree + 3)->left = tree + 7;
    tree->value = 20;
    (tree + 1)->value = 10;
    (tree + 2)->value = 30;
    (tree + 3)->value = 5;
    (tree + 4)->value = 15;
    (tree + 5)->value = 25;
    (tree + 6)->value = 40;
    (tree + 7)->value = 1;

    printf("%d\n" , get_max_width(tree));

    free(tree);
    return EXIT_SUCCESS;
}

 

修改记录

2024.10.13 修改结构体和指针命名,修改求二叉树宽度bug。

题外话 

        由于不会编写makefile,用include的方式引入相关的方法,也算的上一个不被推荐的方式。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值