一种用C实现的hashmap

前言      

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

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

简单介绍

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

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

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

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

        

        

代码

my_map.h

#pragma once
#include <stdlib.h>

#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

struct my_map_node
{
    // 链表长度
    int list_len;

    void *key;

    void *value;

    struct my_map_node *next;  
} ;

// arr + list
struct my_map_body
{
    //容量
    int capacity;

    //数组大小
    int arr_len;

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

    // node 数组
    struct my_map_node *node_arr;

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



typedef void *my_map;

typedef struct my_map_body *my_mapp;

typedef struct my_map_node *my_mapnodep;

// 默认的hashcode 及内存地址
static int mymap_hash_code(void *key);

// 通过key获取对应到数组的下标
static int mymap_get_index(my_mapp mapp, void *key);

// 删除链表中元素,因为数据存放在数组上,一个节点时不做物理删除
static my_mapnodep mymap_delete_list(my_mapnodep head , my_mapnodep be_del);

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

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

// 扩容map
static my_mapnodep mymap_resize(my_mapp mapp);

// 链表插入,尾插
static void mymap_insert_list(my_mapnodep header , my_mapnodep be_insert);

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

//初始化方法,hashcode需要自定义
my_map mymap_init_hash(int hashcode(void *key) , int compare(void *key , void *search));

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

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

// 存数据
void *mymap_put(my_map , void *key , void *value);

// 取数据
void *mymap_get(my_map , void *key);

// 删除数据
void *mymap_delete(my_map , void *key);

// 获取map的size
int mymap_size(my_map);

//callback 0 continue , 1 break ,遍历map
void mymap_foreach(my_map , int callback(void *key, void *value));

my_map.c

​
#include "my_map.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(my_mapp mapp, void *key)
{
    int hashcode = mapp->hashcode(key);
    //int index = hashcode ^ (mapp->arr_len - 1);
    int index = hashcode & (mapp->arr_len - 1);
    return index;
}

my_map mymap_init(int compare(void *key , void *search))
{

    my_mapp map_p = calloc(1 , sizeof(struct my_map_body));
    if(!map_p)
    {
        return NULL;
    }
    map_p->compare = compare;

    map_p->arr_len = INIT_ARR_LEN;
    map_p->node_arr = calloc(INIT_ARR_LEN , sizeof(struct my_map_node));

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

    map_p->hashcode = mymap_hash_code;

    return map_p;
}

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

    my_mapp map_p = mymap_init(compare);
    if(map_p == NULL)
        return NULL;

    map_p->hashcode = hashcode;

    return map_p;
}

void *mymap_put(my_map map, void *key , void *value)
{
    if(key && map)
    {
        my_mapp mapp = map;

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

        int index = mymap_get_index(mapp, key);

        if((headp = (mapp->node_arr + index)) && headp->key)
        {
            currp = headp;
            while(currp)
            {
                if(key == currp->key || !mapp->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 my_map_node));
            if(!pre->next)
                return (void*)-1;

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

            ++ headp->list_len;
            ++ mapp->capacity;

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

            ++ mapp->capacity;
        }

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

void *mymap_get(my_map map, void *key)
{
    if(key && map)
    {
        my_mapp mapp = map;

        int index = mymap_get_index(mapp, key);

        my_mapnodep nodep = NULL;
        if(nodep = (mapp->node_arr + index))
        {
            while(nodep && nodep->key)
            {
                if(key == nodep->key || !mapp->compare(key , nodep->key))
                {
                    return nodep->value;
                }
                nodep = nodep->next;
            }
        }
    }
    return NULL;
}

void *mymap_delete(my_map map, void *key)
{
    if(key && map)
    {
        my_mapp mapp = map;
        my_mapnodep nodep = NULL , be_del = NULL;
        void *old_value;

        int index = mymap_get_index(mapp, key);

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

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

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

static my_mapnodep mymap_delete_list(my_mapnodep head , my_mapnodep be_del)
{
    my_mapnodep 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(my_mapnodep header , my_mapnodep be_insert)
{
    while(header && header->next)
    {
        header = header->next;
    }
    header->next = be_insert;
}


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

static my_mapnodep mymap_free_list_atomic(my_mapnodep head)
{
    my_mapnodep 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;
}

my_map mymap_free(my_map map)
{
    if(map)
    {
        my_mapp mapp = map;
        for(int i = 0; i < mapp->arr_len ; ++ i)
        {
            mymap_free_list(mapp->node_arr + i);
        }

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

        mapp = NULL;
    }
    return NULL;
}

static my_mapnodep mymap_resize(my_mapp mapp)
{
    if(mapp->capacity >= MAX_CAPACITY || mapp->arr_len == MAX_ARR_LEN)
        return mapp->node_arr;

    my_mapnodep newnodep = calloc(mapp->arr_len << 1, sizeof(struct my_map_node));
    if(!newnodep)
        return mapp->node_arr;

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

    for(int i = 0; i < mapp->arr_len; ++ i)
    {
        temp_nodep = mapp->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(mapp->arr_len & mymap_hash_code(temp_nodep->key))
            {
                if(!new_headp)
                {
                    new_headp = (newnodep + i + mapp->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(mapp->node_arr);


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

my_map mymap_free_atomic(my_map map)
{
    if(map)
    {
        my_mapp mapp = map;
        for(int i = 0; i < mapp->arr_len ; ++ i)
        {
            mymap_free_list_atomic(mapp->node_arr + i);
        }

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

        mapp = NULL;
    }
    return NULL;
}

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

void mymap_foreach(my_map map , int callback(void *key, void *value))
{
    if(map)
    {
        my_mapp mapp = map;
        my_mapnodep nodep = NULL , temp = NULL;
        for(int i = 0; i < mapp->arr_len ; ++ i)
        {
            nodep = (mapp->node_arr + i);
            //printf("\n%d,%d\n" , i, nodep->list_len);
            while(nodep && nodep->key)
            {
                temp = nodep->next;
                if(callback(nodep->key , nodep->value))
                {
                    break;
                }
                nodep = 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 = key;
    int j = 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";

    my_map 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()
{
    my_map 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()
{
    my_map 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;
}

​

来个测试截图

求一棵二叉树的宽度

/**
 如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)
22.2.21 更新获取宽度的实现
 */

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




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

typedef struct Node *nodep;

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

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

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

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

    nodep node = NULL;
    nodep left = NULL;
    nodep right = NULL;

    int curlevel = 0;

    while(mylist_list_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;
        }
        else
        {
            ++ cur_width;
        }

        max_width = MAX(max_width,cur_width);
    }

    mylist_free(queue);
    mymap_free(level_map);

    return max_width;
}

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

    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;
}

 

题外话 

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值