前些天看到一个不错的hash table 的不错的实现方案,该哈希表的大小能够自适应的调整,当往哈希表插入了数据数等于哈希表的大小时,此时如果继续插入数据,冲突会增高,通过采用将哈希表扩大二倍,然后呢将原始已经插入原先表的数据重新插入新的哈希表中,但是存在一个问题:
1.该哈希函数需要在表扩大二倍时,仍然能够在该表中呈现为均分分布。所以哈希函数的选择至关重要,这样冲突率很低,
2.如何设计非常合理的结构,让哈希表操作方便,是我们考虑的重点
接下来是我的代码:
//htable.h
#ifndef __HTABLE_H
#define __HTABLE_H
/************************************************
* @aurhor Jacky Lau
* @data 11/01/2014
*
************************************************/
/*===================STRUCT==================================*/
typedef struct HTBALE_INFO {
char *key;
char *value;
struct HTBALE_INFO *prev;
struct HTBALE_INFO *next;
}HTBALE_INFO;
typedef struct HTBALE {
int size;
int used;
struct HTBALE_INFO **data;
}HTABLE;
/*========================FUNCTION================================*/
HTABLE *htable_create(int size);
void htable_destroy(HTABLE *ht);
unsigned hash_function(const char *s, unsigned size);
void htable_growth(HTABLE *ht);
void htable_link(HTABLE *ht, HTBALE_INFO *element);
void htable_insert(HTABLE *ht, char *key, char *value);
void htable_delete(HTABLE *ht, const char *key);
char *htable_find(HTABLE *ht, const char *key);
HTBALE_INFO *htable_locate(HTABLE *ht, const char *key);
void htable_list_free(HTBALE_INFO **list);
HTBALE_INFO **htable_list(HTABLE *ht);
#endif
//htable.c
/************************************************
* @aurhor Jacky Lau
* @data 11/01/2014
*
************************************************/
#include"htable.h"
#include<malloc.h>
#include<string.h>
#include<assert.h>
/********************************************************************************
*
* ______
* |size|
* |used|
* |data|----------->-----
* 0 |----| |----- \
* 1 |----| \|/ \
* 2 |----|------->------ \ ---> |----
* 3 |----| |----| \ / |----|
* . |----| |----| \/ |----|
* . |----| prev |NULL| /\----- |----|
* . |----| next |----|---- |NULL|
* size-1|----|
*
*
*
**********************************************************************************/
HTABLE *htable_create(int size)
{
HTABLE *ht = (HTABLE *)malloc(sizeof(HTABLE));
ht->size =size;
ht->used = 0;
ht->data = (HTBALE_INFO **)malloc(size * sizeof(HTBALE_INFO *));
memset(ht->data, 0, size * sizeof(HTBALE_INFO *));
return ht;
}
void htable_destroy(HTABLE *ht)
{
HTBALE_INFO *h;
HTBALE_INFO *next;
int i;
for(i = 0; i < ht->size; i++)
{
for(h = ht->data[i]; h; h =next)
{
next = h->next;
free(h->key);
free(h->value);
free(h);
}
}
free(ht->data);
free(ht);
}
unsigned hash_function(const char *s, unsigned size)
{
unsigned long h = 0;
unsigned long g;
/*
* From the "Dragon" book by Aho, Sethi and Ullman.
*/
while (*s) {
h = (h << 4) + *s++;
if ((g = (h & 0xf0000000)) != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h % size);
}
//hash function needs to adjust automatically by the hash table size;
//so we need to rehash the key
void htable_growth(HTABLE *ht)
{
int old_size = ht->size;
ht->size = 2 * ht->size;
ht->used = 0;
HTBALE_INFO ** h = (HTBALE_INFO **)malloc(ht->size *sizeof(HTBALE_INFO *));
memset(h, 0 , ht->size * sizeof(HTBALE_INFO *));
HTBALE_INFO **p = ht->data;
HTBALE_INFO **old_entry = ht->data;
ht->data =h;
int i;
HTBALE_INFO *ptr;
HTBALE_INFO *next;
for(i = 0; i < old_size; i++)
{
for(ptr = *p++; ptr; ptr = next)
{
next = ptr->next;
htable_link(ht, ptr);
}
}
free(old_entry);
}
void htable_link(HTABLE *ht, HTBALE_INFO *element)
{
assert(ht);
assert(element);
element->next = NULL; //when we are coding, we must set pointer to null
element->prev = NULL; //especially there are so many malloc and free function
int index = hash_function(element->key, ht->size);
if(ht->data[index])
{
ht->data[index]->prev = element;
element->next = ht->data[index];
}
ht->data[index] = element;
ht->used++;
}
void htable_insert(HTABLE *ht, char *key, char *value)
{
assert(ht);
assert(key);
assert(value);
if(ht->used >= ht->size)
htable_growth(ht);
HTBALE_INFO *h = (HTBALE_INFO *)malloc(sizeof(HTBALE_INFO));
h->key = key;
h->value = value;
// h->next = NULL;
// h->prev =NULL;
htable_link(ht, h);
}
void htable_delete(HTABLE *ht, const char *key)
{
HTBALE_INFO *h = htable_locate(ht, key);
if(h != NULL)
{
if(h->prev == NULL)
ht->data[hash_function(key, ht->size)] = NULL;
else if(h->next == NULL)
h->prev->next = NULL;
else
{
h->next->prev = h->prev;
h->prev->next = h->next;
}
free(h->key);
free(h->value);
free(h);
ht->used--;
}
else
{
printf("do not have this key\n");
}
}
char *htable_find(HTABLE *ht, const char *key)
{
int index = hash_function(key, ht->size);
HTBALE_INFO *h = ht->data[index];
for(; h && (strcmp(h->key, key) != 0); h = h->next);
if(h == NULL)
return NULL;
return h->value;
}
HTBALE_INFO *htable_locate(HTABLE *ht, const char *key)
{
int index = hash_function(key, ht->size);
HTBALE_INFO *h = ht->data[index];
for(; h && (strcmp(h->key, key) != 0); h = h->next);
if(h == NULL)
return NULL;
return h;
}
HTBALE_INFO **htable_list(HTABLE *ht)
{
assert(ht);
HTBALE_INFO **list = (HTBALE_INFO **)malloc(sizeof(*list) *(ht->used +1));
memset(list, 0 , sizeof(HTBALE_INFO *) *(ht->used +1));
int i;
HTBALE_INFO *h;
int count = 0;
for(i = 0; i < ht->size; i++)
{
for(h = ht->data[i]; h; h = h->next)
{
list[count++] = h;
}
}
list[count] = NULL;
return list;
}
void htable_list_free(HTBALE_INFO **list)
{
assert(list);
free(list);
}
//main.c
/************************************************
* @aurhor Jacky Lau
* @data 11/01/2014
*
************************************************/
#include"htable.h"
#include<malloc.h>
#include<stdio.h>
void test_htable()
{
HTABLE * ht = htable_create(10);
// HTBALE_INFO *h = (HTBALE_INFO *)malloc(sizeof(HTBALE_INFO));
int i;
for(i = 0; i < 100; i++)
{
char *key = (char *)malloc(16);
snprintf(key, 16, "key is %d", i);
char *value = (char *)malloc(16);
snprintf(value, 16, "value is %d",i);
htable_insert(ht, key ,value);
//printf("hash_function(%s): %d\n", key, hash_function(key, ht->size));
}
char *search_key = (char *)malloc(16);
// for(i = 0; i < 20; i++)
// {
// snprintf(search_key, 32, "key is %d", i);
// if(htable_find(ht, search_key) != NULL)
// printf("%s\n", htable_find(ht, search_key));
// }
for(i = 0; i < 10; i++)
{
snprintf(search_key, 16, "key is %d", i);
htable_delete(ht, search_key);
}
free(search_key);
HTBALE_INFO **list = htable_list(ht);
for(i = 0; i < ht->used; i++)
printf("%s %s\n", list[i]->key, list[i]->value);
htable_list_free(list);
printf("table size %d, used %d\n", ht->size, ht->used);
htable_destroy(ht);
}
int main()
{
test_htable();
return 1;
}
运行结果如下;