下面这个散列表的实现来自K&R,很经典。在其他场景中遇到的实现更复杂,基本原理不变,只是在hash算法,或者在快速查询上做了优化。
#include
<stdio.h>
#include
<stdlib.h>
//具有相同hash值构成的链表
struct
nlist{
struct
nlist *
next
;
char
*
name
;
//key-定义的名字
char
*
defn
;
//value-替换文本
};
#define
HASHSIZE 101
//桶的大小
static
struct
nlist *hashtable[HASHSIZE];
//hash table
//字符串hash函数
unsigned
hash(
char
*s){
unsigned
hashval;
for
(hashval = 0; s !=
'\0'
; s++)
hashval = *s + hashval * 31;
//the seed can be 1313,131313 etc..
return
hashval % HASHSIZE;
}
//查找函数,在 hashtable中找字符串s所在的bucket
struct
nlist *lookup(
char
*s){
struct
nlist *np;
for
(np = hashtable[hash(s)]; np; np = np->
next
)
if
(strcmp(s, np->
name
) == 0)
return
np;
return
NULL;
}
//加入函数,将(name, defn)加入到 hashtable中,如果已经存在则更新
//如果无足够空间申请表项则返回空
struct
nlist *install(
char
*name,
char
*defn){
struct
nlist *np;
unsigned
hashval;
if
((np = lookup(name)) == NULL){
//要插入的key不存在
np = (
struct
nlist *)malloc(
sizeof
(
struct
nlist));
if
(np == NULL || (np->
name
= strdup(name)) == NULL)
return
NULL;
hashval = hash(name);
//放入相应的桶中
np->
next
= hashtable[hashval];
hashtable[hashval] = np;
}
else
{
//已存在,则更新
free((
void
*)np->
defn
);
}
//最后统一处理 value
if
((np->
defn
= strdup(defn) ) == NULL)
return
NULL;
return
np;
}
//从哈希表中删除一个key-value
void
undef(
char
*s){
unsigned
h;
struct
nlist *prev, *np;
prev = NULL;
h = hash(s);
for
(np = hashtable[h]; np != NULL; np = np->
next
){
if
(strcmp(s, np->
name
) == 0)
break
;
//找到相应的结点
prev = np;
// 保留目标结点的前一个结点
}
if
(np != NULL){
if
(prev == NULL)
//说明要删除的是桶的第一个成员
hashtable[h] = np->
next
;
else
prev->
next
= np->
next
;
free((
void
*)np->
name
);
//切记要分别释放
free((
void
*)np->
defn
);
free((
void
*)np);
}
}