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