链接法,即把散列到同一槽中的所有元素都放在一个链表中。 链表是无序的,在查找一个元素时需要遍历链表。 对于删除函数,假如参数是要删除的结点,那么如果链表是双向的,删除操作可以O(1)内完成。 在下面的删除函数中,参数是关键字,这样更为方便。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 20
// 链表结点的定义
typedef struct _ListNode {
struct _ListNode *prev, *next;
char *key;
char *val;
} ListNode;
// 定义全局的各个槽链表的指针数组
ListNode *hashmap[SIZE];
// 这里的散列函数与Java中String及HashMap中的散列相同
// 注意为了保证向右逻辑运算(用0而不是符号位补高位)
// 要将h声明为无符号的
unsigned hashcode(char *key)
{
// Ensure >> is logical shift
unsigned h = 0;
// String.hashcode()
do h = 31 * h + *key++;
while (*key != '\0');
// HashMap.hash()
h ^= (h >> 20) ^ (h >> 12);
return h ^ (h >> 7) ^ (h >> 4);
}
ListNode * hashmap_search(char *key)
{
unsigned h = hashcode(key) % SIZE;
ListNode *node = hashmap[h];
while (node != NULL) {
// 当相同时,strcmp返回0
if (strcmp(node->key, key) == 0) {
return node;
}
node = node->next;
}
return NULL;
}
char * hashmap_insert(char *key, char *val)
{
unsigned h = hashcode(key) % SIZE;
printf("Insert %s - %s to bucket %d\n", key, val, h);
// Find duplicate key, replace it then return old value
ListNode *node = hashmap_search(key);
if (node != NULL) {
char *oldVal = node->val;
node->val = val;
return oldVal;
}
// Not found, create new node to save key&val pair
ListNode *newNode = malloc(sizeof(ListNode));
newNode->prev = NULL;
newNode->key = key;
newNode->val = val;
if (hashmap[h] == NULL) {
hashmap[h] = newNode;
} else {
hashmap[h]->prev = newNode;
newNode->next = hashmap[h];
hashmap[h] = newNode;
}
return val;
}
char * hashmap_delete(char *key)
{
ListNode *node = hashmap_search(key);
if (node != NULL) {
// Set prev node's next to node's next
if (node->prev == NULL) {
unsigned h = hashcode(key) % SIZE;
hashmap[h] = node->next;
} else {
node->prev->next = node->next;
}
// Set next node's prev to node's prev
if (node->next != NULL)
node->next->prev = node->prev;
return node->val;
}
return NULL;
}
void hashmap_print()
{
int i;
for (i = 0; i < SIZE; i++) {
ListNode *node = hashmap[i];
while (node != NULL) {
printf("%s - %s, ", node->key, node->val);
node = node->next;
}
if (hashmap[i] != NULL)
printf("%d bucket\n", i);
}
printf("\n");
}
int main(void)
{
// Compare to String.hashcode() in JDK
printf("%d\n", hashcode("helloworld"));
hashmap_insert("aabb", "value1");
hashmap_insert("ccdd", "value2");
hashmap_insert("i'mcdai", "value3");
int i;
for (i = 0; i < 2 * SIZE + 5; i++) {
char *key = calloc(sizeof(char), 10);
char *val = calloc(sizeof(char), 10);
sprintf(key, "%s%d", "aabbcc", i);
sprintf(val, "%s%d", "val ", i);
hashmap_insert(key, val);
}
// Insert duplicate key
printf("%s\n", hashmap_insert("i'mcdai", "dupdup"));
hashmap_print();
printf("%s\n", hashmap_search("i'mcdai")->val);
printf("%s\n", hashmap_search("aabbcc18")->val);
//printf("%s\n", hashmap_search("NOTFOUND")->val);
// All in bucket 18: aabbcc10 => aabbcc0 => i'mcdai
printf("%s\n", hashmap_delete("aabbcc10"));
printf("%s\n", hashmap_delete("i'mcdai"));
hashmap_print();
return 1;
}