用散列表存储数据在理论上能做到常量级的查询或插入时间复杂度。散列表中最核心的是求取哈希值。有集合R、有限整数集Z,将R映射到Z的函数称为散列函数。
整数的散列函数可以为取模函数,因为整数域中的任何元素总可以通过取模操作被限制到一定范围内;
字符串的散列函数有很多,不一一列举;
结构体的散列函数则可以视结构体的具体结构而定。
......
题为“通用散列表”,所以这里实现的散列表并不依赖于具体的数据类型,本实现仅提供散列表查询和插入的接口,具体的散列函数需由调用该接口的用户提供。
参考源码:
#define MAX_SIZE 10009
#define SAME 0
#define TRUE 1
#define FALSE 0
struct HashNode
{
void* value;
struct HashNode* next;
};
static struct HashNode* g_hash_table[MAX_SIZE];
typedef unsigned int (*func_hash_value)(void* value);
typedef int (*func_cmp)(void*, void*);
int hash_insert_value(void* value, func_hash_value hash_value, func_cmp cmp)
{
unsigned int val = hash_value(value) % MAX_SIZE;
struct HashNode* p = g_hash_table[val];
for (; p != NULL; p = p->next)
if (cmp(p->value, value) == SAME)// the value is already insert into the table
return 0;
if (!(p = (struct HashNode*)malloc(sizeof(struct HashNode))))
return -1;// insert failed
p->value = value;
p->next = g_hash_table[val];
g_hash_table[val] = p;
return 0;
}
int hash_has_value(void* value, func_hash_value hash_value, func_cmp cmp)
{
unsigned int val = hash_value(value) % MAX_SIZE;
struct HashNode* p = g_hash_table[val];
for (; p != NULL; p = p->next)
if (cmp(p->value, value) == SAME)
return TRUE;
return FALSE;
}
func_hash_value为散列函数,func_cmp比对函数。这两个函数均需由用户提供。
下列源码提供了字符串散列表的测试例程。
unsigned int bkdr_hash_value(char *str)
{
unsigned int seed = 131;// 31 131 1313 13131 131313 ...
unsigned int hash = 0;
while (*str)
hash = hash * seed + (*str++);
return (hash & 0x7FFFFFFF);
}
int string_cmp_func(void* a, void* b)
{
return strcmp((char*)a, (char*)b);
}
int main(int argc, char* argv[])
{
int n, a, i;
char* str = NULL;
char s[100];
memset(g_hash_table, 0, sizeof(g_hash_table));
scanf("%d", &n);
while (n--)
{
str = (char*)malloc(100);
scanf("%s", str);
hash_insert_value((void*)str, (unsigned int(*)(void*))bkdr_hash_value, string_cmp_func);
}
printf("hash table already created. now input the times you want to search\n");
scanf("%d", &n);
while (n--)
{
scanf("%s", s);
printf("value %s %s\n", s,
hash_has_value((void*)s, (unsigned int(*)(void*))bkdr_hash_value, string_cmp_func) == TRUE ? "exist" : "not exit");
}
return 0;
}
hash_has_value((void*)s, (unsigned int(*)(void*))bkdr_hash_value, string_cmp_func),此处可能不太好理解,” (unsigned int(*)(void*))bkdr_hash_value“是什么东西?这种用法确实不常见,但如果不在bkdr_hash_value前加那么一段可能编译就通不过,因为函数类型与所要求的并不一致,所以前面的那一段实际上是强制类型转换。