《算法导论》第11章 散列表 (2)散列表

时间:2021-08-10 19:17:07


用散列表来解决直接寻址表的那两个问题。但由此带来的散列值的碰撞问题。 最简单的解决方法是链接法,以及下一节介绍的开放寻址法。
链接法,即把散列到同一槽中的所有元素都放在一个链表中。 链表是无序的,在查找一个元素时需要遍历链表。 对于删除函数,假如参数是要删除的结点,那么如果链表是双向的,删除操作可以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;
}