本文提供一种C语言HASH表的实现方法,包括插入、删除、查找操作,但不包括hash值的计算方法。
引用:
C语言快速判断素数的两种方法
一、基本结构
- 每个cell仅记录第一个元素,采用单向链表方式处理hash冲突。
- hash表大小使用素数,即cell数组的大小。
二、定义
hash表仅需记录cell数组指针、数组大小,cell仅需记录同hash值的第一个元素,如果没有元素则为NULL。
typedef struct hash_cell_struct hash_cell_t;
typedef struct hash_table_struct hash_table_t;
struct hash_cell_struct
{
void* data;
};
struct hash_table_struct
{
hash_cell_t* arr;
uint32 size;
};
用户定义的数据结构体还需要增加一个void * 类型的指针,用于处理hash冲突的单向链表。如果一个结构体同时存在多个hash表中,那么相应的也需要多个void * 指针。
三、接口
1. 创建hash表
hash_table_t* hash_create(
uint32 n /* hash表大小 */
)
{
hash_table_t* table;
/* 获取合适的实际哈希表大小 */
while (!is_prime_fast(n))
n++;
/* 创建 */
RET_IF_NULL(table = malloc(sizeof(hash_table_t) + n * sizeof(hash_cell_t)), NULL);
table->arr = (hash_cell_t*)(table + 1);
table->size = n;
memset(table->arr, 0, n * sizeof(hash_cell_t));
return table;
}
2. 释放hash表
void hash_free(
hash_table_t* table
)
{
free(table);
}
3. INSERT
/* 功能: 插入元素N到hash表 */
/* TYPE: 数据结构体的类型
* HASH: 处理冲突的void*成员名
* TABLE: hash表
* FOLD: hash值
* N: 要插入的数据结构体 */
#define HASH_INSERT(TYPE, HASH, TABLE, FOLD, N)\
do {\
hash_cell_t* _cell;\
\
_cell = (hash_cell_t*)&((TABLE)->arr[(uint32)FOLD % (TABLE)->size]);\
(N)->HASH = _cell->data;\
_cell->data = N;\
} while (0)
4. DELETE
/* 功能: 在hash表中删除元素N */
/* TYPE: 数据结构体的类型
* HASH: 处理冲突的void*成员名
* TABLE: hash表
* FOLD: hash值
* N: 要删除的数据结构体 */
#define HASH_DELETE(TYPE, HASH, TABLE, FOLD, N)\
do {\
hash_cell_t* _cell; \
TYPE* _item;\
\
_cell = (hash_cell_t*)&((TABLE)->arr[(uint32)FOLD % (TABLE)->size]); \
if ((N) == _cell->data)\
{\
_cell->data = (N)->HASH;\
}\
else\
{\
_item = _cell->data;\
while (_item->HASH != N)\
_item = _item->HASH;\
_item->HASH = (N)->HASH;\
}\
} while (0)
5. SEARCH
/* 功能: 获取第n个cell的第一个元素 */
#define HASH_GET_NTH_FIRST(TABLE, NCELL) (((hash_cell_t*)&((TABLE)->arr[NCELL]))->data)
/* 功能: 获取下一个元素 */
#define HASH_GET_NEXT(HASH, N) ((N)->HASH)
/* 功能: 在hash表中查找元素 */
/* HASH: 处理冲突的void*成员名
* TABLE: hash表
* FOLD: hash值
* N: 要删除的数据结构体
* TEST: 识别同hash值元素的测试语句 */
#define HASH_SEARCH(HASH, TABLE, FOLD, N, TEST)\
do {\
N = HASH_GET_NTH_FIRST(TABLE, (uint32)FOLD % (TABLE)->size);\
while (N)\
{\
if (TEST)\
break;\
N = HASH_GET_NEXT(HASH, N);\
}\
} while (0)
测例
该hash表一般仅用于查找,并结合链表使用。测例中链表参考C语言通用双向链表。
#include "list.h"
#include "hash.h"
#include <assert.h>
typedef struct item_struct item_t;
struct item_struct
{
uint32 value;
void* hash; /* 处理hash冲突用的单向链表指针域 */
LIST_NODE_T(item_t) link;
};
typedef LIST_BASE_NODE_T(item_t) item_lst_t;
/* 功能: 测试hash表 */
void test_hash()
{
item_lst_t lst_items = { 0 };
hash_table_t* table;
item_t* item;
uint32 i;
table = hash_create(100);
/* 插入1000个元素, 直接以value 0~999作为hash值 */
for (i = 0; i < 1000; i++)
{
item = malloc(sizeof(item_t));
item->value = i;
LIST_ADD_LAST(link, lst_items, item);
HASH_INSERT(item_t, hash, table, i, item);
}
/* 查找, 一定存在, 同时删除其中值为偶数的元素 */
for (i = 0; i < 1000; i++)
{
HASH_SEARCH(hash, table, i, item, item->value == i);
assert(item);
if (i % 2 == 0)
{
HASH_DELETE(item_t, hash, table, i, item);
LIST_REMOVE(link, lst_items, item);
free(item);
}
}
/* 再次查找, 一定只有值为奇数的元素存在, 也删掉 */
for (i = 0; i < 1000; i++)
{
HASH_SEARCH(hash, table, i, item, item->value == i);
if (i % 2 == 0)
{
assert(item == NULL);
}
else
{
assert(item);
HASH_DELETE(item_t, hash, table, i, item);
LIST_REMOVE(link, lst_items, item);
free(item);
}
}
/* 遍历hash表每个CELL, 因为已经删完一定都为空 */
for (i = 0; i < table->size; i++)
{
assert(HASH_GET_NTH_FIRST(table, i) == NULL);
}
hash_free(table);
}