前言
对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的方式引入相关的方法,也算的上一个不被推荐的方式。