Hashtable - C语言实现

程序要点:

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_42263483/article/details/80890554