C语言Hash Table的实现


调用函数hash_create()之后创建一个Hash Table
------------------------------------------------------
hashtable
|---------------|
| gethash()   
-| lh_strhash()
| compare()   
-| equal_str()
| hashsize    
-| size
| count       
-| 0
| **hashlist   -|------>|------------------|
|---------------|    
--|struct hashentry *| NULL
|------------------|
|                  |
|------------------|
|                  |
|------------------|
|                  |
|------------------|
|                  |
|------------------|
|struct hashentry *| NULL
|------------------|


调用函数hash_insert()时如果出现冲突,则形成hashentry队列
------------------------------------------------------
hashtable
|-------------|
| gethash()   | lh_strhash()
| compare()   | equal_str()
| hashsize    | size
| count       | 2
| **hashlist
-|---->|------------------|
|-------------|     |struct hashentry *| NULL
|------------------|
|                  |
|------------------|    hashentry     hashentry
|                  |-->|---------|   |---------|
|------------------|   | *key  
-|   | *key   -|
|                  |   | *data
--|   | *data --|
|------------------|   | *next
--|-->| *next --| NULL
|                  |   |---------|   |---------|
|------------------|
|struct hashentry *| NULL

|------------------|



hash.h
--------------------------------------
#ifndef _LINUX_GHASH_H_
#define _LINUX_GHASH_H_
#include <string.h>

#ifndef __USE_ISOC99
#define inline
#endif

#define create_hashtable(hsize) \
hash_create(lh_strhash, equal_str, hsize)

unsigned int lh_strhash(void *src);
int equal_str(void *k1, void *k2);

struct hashentry;
struct _hashtable;
typedef struct _hashtable   hashtable;


hashtable *hash_create(unsigned int (*keyfunc)(void *),
int (*comparefunc)(void *,void *),
int size);
void hash_free(hashtable *tab);
void hash_insert(void *key, void *data, hashtable *tab);
void hash_remove(void *key, hashtable *tab);
void *hash_value(void *key, hashtable *tab);
void hash_for_each_do(hashtable *tab, int (cb)(void *, void *));
int hash_count(hashtable *tab);

#endif




hash.c
--------------------------------------
#include <string.h>
#ifdef   DMALLOC
#include <dmalloc.h>
#else
#include <stdlib.h>
#endif
#include "hash.h"
#ifndef __USE_ISOC99
#define inline
#endif

struct hashentry
{
void             *key;
void             *data;
struct hashentry *next;
};

struct _hashtable
{
unsigned int      (*gethash)(void *);
int               (*compare)(void *, void *);
int               hashsize;
int               count;
struct hashentry **hashlist;
};

#define hashindex(key, tab) ((tab->gethash)(key) % (tab->hashsize -1))

unsigned int lh_strhash(void *src)
{
int i, l;
unsigned long ret = 0;
unsigned short *s;
char *str = (char *)src;
if (str == NULL)
return(0);
l = (strlen(str) + 1) / 2;
s = (unsigned short *)str;

for (i = 0; i < l; i++)
ret ^= s[i]<<(i&0x0f);

return(ret);
}

int equal_str(void *k1, void *k2)
{
return (0 == strcmp((char *)k1, (char *)k2));
}

inline struct hashentry *hashentry_new(void *key, void *data)
{
struct hashentry *new = malloc(sizeof(struct hashentry));
new->key = key;
new->data = data;
new->next = NULL;
return new;
}

void hlist_append(struct hashentry **root, void *key, void *data)
{
struct hashentry *l, *pos;
l = hashentry_new(key, data);
if (*root == NULL) {
*root = l;
} else {
for(pos = *root; pos->next != NULL; pos = pos->next);
pos->next = l;
}
}

int hlist_update(struct hashentry *root, void *key, void *data,
int (*compare)(void *, void *))
{
struct hashentry *pos;
for(pos = root; pos != NULL; pos = pos->next ) {
if ( compare(key, pos->key) ) {
free(pos->data);    
pos->data = data;
free(key);
return 0;
}
}
return -1;
}

inline struct hashentry *hashentry_free(struct hashentry *h)
{
struct hashentry *next = h->next;
free(h->key);
free(h->data);
free(h);
h = NULL;
return (next);
}

int hlist_remove(struct hashentry **root, void *key,
int (*compare)(void *,void *))
{
struct hashentry *pos ,*prev;

if (NULL == *root) return -1;

if (compare((*root)->key, key)) {
*root = hashentry_free(*root);
return 0;
}

prev = *root;
for (pos = prev->next; NULL != pos; pos = pos->next) {
if (compare(pos->key, key)) {
prev->next = hashentry_free(pos);
return 0;
}
prev = pos;
}
return -1;
}

hashtable *hash_create(unsigned int (*keyfunc)(void *),
int (*comparefunc)(void *, void *),
int size)
{
int len = sizeof(struct hashentry *) * size;
int i;
hashtable *tab = malloc( sizeof(hashtable) );
memset(tab, 0, sizeof(hashtable));
tab->hashlist = malloc(len);

if (tab->hashlist == NULL) {
free(tab);
return NULL;
}

memset(tab->hashlist, 0, len );
for (i = 0; i < size; i++)
tab->hashlist[i] = NULL ;

tab->compare = comparefunc;
tab->gethash = keyfunc;
tab->hashsize = size;
tab->count    = 0;
return tab;
}

void hash_free(hashtable *tab)
{
int i;
struct hashentry *pos;

for (i = 0; i < tab->hashsize; i++)
for (pos = tab->hashlist[i]; NULL != pos; pos = hashentry_free(pos));

free(tab->hashlist);
free(tab);
tab =NULL;
}

void hash_insert(void *key, void *data, hashtable *tab)
{
unsigned int index = hashindex(key, tab);
struct hashentry *root = tab->hashlist[index];

if ( hlist_update(root, key, data, tab->compare ) != 0 ) { //(1)
hlist_append(&(tab->hashlist[index]), key, data );
tab->count++;
}
}

//(1) 查看Hash Table中是否存在键值为key的项,如果有则替换该键值所对应的value,否则调用hlist_append为key, data生成新的hashentry并插入相应的队列中。

void hash_remove(void *key, hashtable *tab)
{
unsigned int index = hashindex(key, tab);
if (hlist_remove(&(tab->hashlist[index]), key, tab->compare) == 0) {
tab->count--;
}
}

void *hash_value(void *key, hashtable *tab)
{
struct hashentry *pos;
unsigned int index = hashindex(key, tab);
for (pos = tab->hashlist[index]; NULL != pos; pos = pos->next) {
if (tab->compare(key, pos->key)) {
return (pos->data);
}
}
return NULL;
}


void hash_for_each_do(hashtable *tab, int(cb)(void *, void *))
{
int i = 0;
struct hashentry *pos;
for (i = 0; i < tab->hashsize; i++) {
for (pos = tab->hashlist[i]; NULL != pos; pos = pos->next ) {
cb(pos->key, pos->data);
}
}
}

inline int hash_count(hashtable *tab)
{
return tab->count;
}



test.c
--------------------------------------
#ifdef DMALLOC
#include <dmalloc.h>
#else
#include <stdlib.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include "hash.h"

#define maxhash 10

int work(void *key, void *data)
{
printf("%s->%s\n",(char *)key, (char *)data);
return 0;
}

int main(int argc, char *argv[])
{
int i;

char key[20];
char data[20];

memset(key, 0, 20);
memset(data, 0, 20);
hashtable *tab = create_hashtable(10);
for (i = 0; i < maxhash; i++) {
sprintf(key, "key%d", i);
sprintf(data, "the line no: %d", i);
hash_insert((void *)strdup(key), (void *)strdup(data), tab);
}

printf("remove key4\n");
hash_remove("key4", tab);
printf("key -> value\n");
for (i = 0; i < maxhash; i++) {
sprintf(key, "key%d", i);
printf("%s->%s\n", key, (char *)hash_value(key, tab));
}
printf("\n");

printf("%s->%s\n", "this is ", (char *)hash_value("this is ", tab));

printf("\n");
hash_for_each_do(tab, work);
printf("size %d\n", hash_count(tab));
hash_free(tab);
exit(0);
}


编译运行
--------------------------------------
$ gcc test.c hash.c
$ ./a.out
remove key4
key -> value
key0->the line no: 0
key1->the line no: 1
key2->the line no: 2
key3->the line no: 3
key4->(null)
key5->the line no: 5
key6->the line no: 6
key7->the line no: 7
key8->the line no: 8
key9->the line no: 9

this is ->(null)

key6->the line no: 6
key1->the line no: 1
key0->the line no: 0
key9->the line no: 9
key3->the line no: 3
key8->the line no: 8
key2->the line no: 2
key5->the line no: 5
key7->the line no: 7
size 9
posted @ 2011-04-29 16:18  ACE封印  Views(2193)  Comments(0Edit  收藏  举报