前言`
请手动实现一个固定长度,基于拉链法的哈希表。包含创建、销毁、插入、删除、查找等基本操作。
一、头文件
#ifndef HASH_MAP_H
#define HASH_MAP_H
#define _CRT_SECURE_NO_WARNINGS
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define HASHMAP_CAPACITY 10
typedef char* KeyType;
typedef char* ValueType;
//键值对结点
typedef struct node_s {
KeyType key;
ValueType val;
struct node_s* next;
}KeyValueNode;
//表结构体
typedef struct {
//哈希桶
KeyValueNode* bucket[HASHMAP_CAPACITY];
//哈希函数需要的种子值
uint32_t hash_seed;
}HashMap;
//创建
HashMap* hashmap_create();
//插入
ValueType hashmap_put(HashMap *map,KeyType key,ValueType val);
//查询一个键值对
ValueType hashmap_get(HashMap* map, KeyType key);
//删除一个键值对
bool hashmap_remove(HashMap* map, KeyType key);
//销毁
void hashmap_destroy(HashMap* map);
#endif // !HASH_MAP_H
二、基本操作
#include "hash_map.h"
/* murmur_hash2 */
static uint32_t hash(const void* key, int len, uint32_t seed) {
const uint32_t m = 0x5bd1e995;
const int r = 24;
uint32_t h = seed ^ len;
const unsigned char* data = (const unsigned char*)key;
while (len >= 4) {
uint32_t k = *(uint32_t*)data;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
switch (len) {
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0];
h *= m;
};
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
//创建
HashMap* hashmap_create() {
HashMap *map=calloc(1, sizeof(HashMap));
if (map == NULL) {
printf("error:calloc failed in hashmap_create.\n");
exit(1);
}
map->hash_seed = time(NULL);
return map;
}
//插入一个键值对
ValueType hashmap_put(HashMap* map, KeyType key, ValueType val) {
//通过哈希函数确定在哪个桶中
int idx = hash(key, strlen(key), map->hash_seed) % HASHMAP_CAPACITY;
//遍历找到的哈希桶,查找key是否重复
KeyValueNode* curr = map->bucket[idx];
while (curr) {
if (strcmp(key, curr->key)==0) {
//key已存在,更新val,返回旧val
ValueType old_val = curr->val;
curr->val = val;
return old_val;
}
curr = curr->next;
}
//key不重复就重新插入
KeyValueNode* new_node = malloc(sizeof(KeyValueNode));
if (new_node == NULL) {
printf("Error: malloc failed in hashmap_put.\n");
exit(1);
}
new_node->val = val;
new_node->key = key;
new_node->next = map->bucket[idx];
map->bucket[idx] = new_node;
return NULL;
}
//查询一个键值对
ValueType hashmap_get(HashMap* map, KeyType key) {
int idx = hash(key, strlen(key), map->hash_seed) % HASHMAP_CAPACITY;
KeyValueNode* curr = map->bucket[idx];
while (curr) {
if (strcmp(curr->key,key)==0) {
return curr->val;
}
curr = curr->next;
}
return NULL;
}
//删除一个键值对
bool hashmap_remove(HashMap* map, KeyType key) {
int idx = hash(key, strlen(key), map->hash_seed) % HASHMAP_CAPACITY;
KeyValueNode* curr = map->bucket[idx];
KeyValueNode* p = NULL;
if (strcmp(curr->key, key) == 0) {
map->bucket[idx] = curr->next;
free(curr);
return true;
}
p = curr;
curr = curr->next;
while (curr) {
if (strcmp(curr->key, key) == 0) {
p->next = curr->next;
free(curr);
return true;
}
p = curr;
curr = curr->next;
}
return false;
}
//销毁
void hashmap_destroy(HashMap* map) {
for (size_t i = 0; i < HASHMAP_CAPACITY; i++) {
KeyValueNode* curr = map->bucket[i];
while (curr) {
KeyValueNode* tmp = curr->next;
free(curr);
curr = tmp;
}
}
free(map);
}
三、测试源
#include "hash_map.h"
int main(void) {
HashMap* map = hashmap_create();
if (map == NULL) {
printf("hashmap_create failed in main.\n");
return 1;
}
//插入键值对
hashmap_put(map,"二氧","化钙");
hashmap_put(map, "氯化", "钠");
hashmap_put(map, "高锰", "酸钾");
//更新键值对
char *old_value=hashmap_put(map, "二氧", "化钛");
printf("更新键值对后返回的旧值为:%s\n",old_value);
printf("根据key值为‘氢氧’查询已有键值对val值为:%s\n", hashmap_get(map, "氢氧"));
printf("根据key值为‘氯化’查询已有键值对val值为:%s\n", hashmap_get(map, "氯化"));
hashmap_remove(map, "氯化");
printf("删除key值为‘氯化’的键值对后再查询为:%s\n", hashmap_get(map, "氯化"));
hashmap_destroy(map);
return 0;
}
四、输出样例
总结
哈希表是一种“数组+链表”的结构。键值对:键、值、next指针。哈希表结构体:哈希桶和种子值。