程序要点:
1. 原则上,谁malloc谁负责free;
2. hash_index的计算:使用key字符串每个字符之和% max_node_index;
3. 不允许添加相同的struct node *n,即对应的key=value已经在table中存在则添加失败;
4. 允许相同的key,但不同的value.以单向链表的形式存放于table->nodes[hash_index]中。
HashTable.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RET_TRUE 0
#define RET_FALSE -1
struct node {
char *key;
char *value;
struct node *next;
};
struct hash_table {
struct node **nodes;
int max_node_index;
int (*hash)(struct hash_table *table, char *key);
int (*search)(struct hash_table *table, struct node *node);
int (*insert)(struct hash_table *table, struct node *node);
int (*delete)(struct hash_table *table, struct node *node);
};
static void free_node(struct node **n);
static struct node * create_node(char *key, char *value);
static int compare_node(struct node *dst, struct node *src);
static int malloc_and_copy_string(char **dst, char *src);
static int malloc_and_copy_node(struct node *dst, struct node *src);
static int hash(struct hash_table *table, char *key);
static int search(struct hash_table *table, struct node *node);
static int insert(struct hash_table *table, struct node *node);
static int delete(struct hash_table *table, struct node *node);
static int init_hashtable(struct hash_table *table, int size);
/*
* Compute the sum of each char value in key.
* hash_index = sum % hash_table.max_node_index
*/
static int hash(struct hash_table *table, char *key)
{
int len, hash_index, char_sum = 0;
char c;
if(NULL == table || table->max_node_index <= 0 || NULL == key)
return RET_FALSE;
//printf("hash for [%s] ", key);
len = strlen(key);
while(len-- > 0) {
c = *key++;
char_sum += (int)c;
}
hash_index = char_sum % table->max_node_index;
//printf("is %d. char_sum=%d, table->max_node_index=%d\n",
// hash_index, char_sum, table->max_node_index);
return hash_index;
}
/*
* @param node->key is set, node->value is null, node->next is null.
*
* @return node->value will be filled with the right value if have.
* Always return the first element which is matched.
*/
static int search(struct hash_table *table, struct node *node)
{
struct node *cur;
int hash_index;
if(NULL == table || NULL == table->nodes
|| NULL == node || NULL == node->key )
return RET_FALSE;
hash_index = table->hash(table, node->key);
if(hash_index == RET_FALSE)
return RET_FALSE;
cur = table->nodes[hash_index];
if(NULL == cur)
return RET_FALSE;
while(cur && 0 != strcmp(cur->key, node->key)) {
cur = cur->next;
}
if(cur) { /* Got the right node */
return malloc_and_copy_string(&node->value, cur->value);
} else {
return RET_FALSE;
}
}
static int compare_node(struct node *dst, struct node *src)
{
if(NULL == dst || NULL == src)
return RET_FALSE;
if( 0 == strcmp(dst->key, src->key)
&& 0 == strcmp(dst->value, src->value) )
return RET_TRUE;
return RET_FALSE;
}
static void free_node(struct node **p)
{
struct node *n = *p;
if(NULL == n)
return;
if(n->key)
free(n->key);
if(n->value)
free(n->value);
n->key = NULL;
n->value = NULL;
free(n);
n = NULL;
}
/*
* *dst should be NULL, we will malloc for it.
*/
static int malloc_and_copy_string(char **dst, char *src)
{
int len;
if(*dst || NULL == src)
return RET_FALSE;
len = strlen(src);
*dst = malloc(len+1);
memset(*dst, 0, len+1);
strncpy(*dst, src, len);
return RET_TRUE;
}
/*
* We will malloc for dst->key and dst->value according to the size of src->key and src->value.
*/
static int malloc_and_copy_node(struct node *dst, struct node *src)
{
int len;
if(NULL == dst || NULL == src)
return RET_FALSE;
if( RET_FALSE == malloc_and_copy_string(&dst->key, src->key)
|| RET_FALSE == malloc_and_copy_string(&dst->value, src->value) ) {
if(dst->key)
free(dst->key);
if(dst->value)
free(dst->value);
return RET_FALSE;
}
dst->next = NULL;
return RET_TRUE;
}
/*
* We will create a total new struct node, to copy the data from struct node *node.
* @param struct node *node should be free outside.
*/
static int insert(struct hash_table *table, struct node *node)
{
struct node **cur;
int hash_index;
if(NULL == table || NULL == table->nodes ||
NULL == node || NULL == node->key || NULL == node->value)
return RET_FALSE;
hash_index = table->hash(table, node->key);
if(hash_index == RET_FALSE)
return RET_FALSE;
cur = &table->nodes[hash_index];
while(*cur) { /* It already has node at table->nodes[hash_index] */
if(RET_TRUE == compare_node(*cur, node)) /* A same node exists in table, so insert will fail. */
return RET_FALSE;
cur = &((*cur)->next);
}
*cur = malloc(sizeof(struct node));
memset(*cur, 0, sizeof(struct node));
return malloc_and_copy_node(*cur, node);
}
/*
* This function will not free @param struct node *node. It should be free outside.
*/
static int delete(struct hash_table *table, struct node *node)
{
struct node **cur, **next, **prev;
int hash_index;
if(NULL == table || NULL == table->nodes ||
NULL == node || NULL == node->key || NULL == node->value)
return RET_FALSE;
hash_index = table->hash(table, node->key);
if(hash_index == RET_FALSE)
return RET_FALSE;
cur = &table->nodes[hash_index];
if(NULL == *cur)
return RET_FALSE;
next = &((*cur)->next);
prev = &table->nodes[hash_index];
do {
if( RET_TRUE == compare_node(*cur, node) ) {
free_node(cur);
*cur = NULL;
(*prev)->next = *next;
return RET_TRUE;
}
prev = cur;
cur = &((*cur)->next);
next = &((*cur)->next)?&((*cur)->next):NULL;
} while(*cur);
return RET_FALSE;
}
static int init_hashtable(struct hash_table *table, int size)
{
if(NULL == table || size <= 0)
return RET_FALSE;
table->nodes = malloc(size * sizeof(struct node *));
if(NULL == table->nodes) {
printf("init: malloc fail with %ld bytes\n", size * sizeof(struct node *));
return RET_FALSE;
}
memset(table->nodes, 0, size * sizeof(struct node *));
table->max_node_index = size;
table->hash = hash;
table->search = search;
table->delete = delete;
table->insert = insert;
return RET_TRUE;
}
static void print_hashtable(struct hash_table *table)
{
int i;
struct node *cur;
if(NULL == table || NULL == table->nodes || table->max_node_index <= 0)
return;
for(i = 0; i < table->max_node_index; i++) {
cur = table->nodes[i];
printf("index %d: ", i);
while(cur) {
printf("[%s]=[%s];", cur->key, cur->value);
cur = cur->next;
}
printf("\n");
}
}
static inline struct node * create_node(char *key, char *value)
{
struct node *n;
n = malloc(sizeof(struct node));
memset(n, 0, sizeof(struct node));
if(key)
malloc_and_copy_string(&n->key, key);
if(value)
malloc_and_copy_string(&n->value, value);
return n;
}
/*
* Test code
*/
int main()
{
struct hash_table mytable;
struct node *n;
memset(&mytable, 0, sizeof(struct hash_table));
init_hashtable(&mytable, 50);
n = create_node("the first", "Ala");
mytable.insert(&mytable, n);
free_node(&n);
n = create_node("the first", "same key but different value");
mytable.insert(&mytable, n);
free_node(&n);
n = create_node("second", "hello my table");
mytable.insert(&mytable, n);
free_node(&n);
n = create_node("third", "333333333333333333333333333333333");
mytable.insert(&mytable, n);
free_node(&n);
n = create_node("Fourth", "44444");
mytable.insert(&mytable, n);
free_node(&n);
n = create_node("the first", "a new value");
mytable.insert(&mytable, n);
free_node(&n);
n = create_node("Fourth", "The fourth value");
mytable.insert(&mytable, n);
free_node(&n);
print_hashtable(&mytable);
printf("Delete one element: [the first]=[same key but different value]\n");
n = create_node("the first", "same key but different value");
mytable.delete(&mytable, n);
free_node(&n);
print_hashtable(&mytable);
printf("Search key=[second], value=[");
n = create_node("second", NULL);
mytable.search(&mytable, n);
printf("%s]\n", n->value);
free_node(&n);
return 0;
}