双链表的查询只能从头往尾找,或者从尾往头找, 效率低下, 所以就有了哈希表。哈希表是将双链表通过一个hash函数分类,分成若干类,形成一个索引,然后查找的时候就可以快速的定位到某一类,减少查询的时间。下面是用哈希表的底层使用内核链表实现的。
.h文件
#pragma once
#include "list.h"
struct hash_table {
struct list_head *tab; /* 哈希表首元素的地址 */
size_t nmemb; /* 哈希表元素个数 */
size_t (*hash)(const struct list_head *node); /* 哈希函数 */
int (*hash_cmp)(const struct list_head *a, const struct list_head *b); /*哈希比较 */
int (*push)(struct hash_table *phtable, struct list_head *node);
struct list_head *(*search)(const struct hash_table *phtable, const struct list_head *key);
int (*del)(struct hash_table *phtable, struct list_head *node);
};
int hash_table_init(struct hash_table *phtable, size_t nmemb,
size_t (*hash)(const struct list_head *node),
int (*hash_cmp)(const struct list_head *a, const struct list_head *b));
int hash_table_destroy(struct hash_table *phtable);
.c文件
#include <stdio.h>
#include "hash_table.h"
#include <assert.h>
#include <stdlib.h>
static int hash_del(struct hash_table *phtable, struct list_head *node)
{
assert(phtable != NULL);
assert(node != NULL);
size_t v = phtable->hash(node) % phtable->nmemb;
struct list_head *cur = phtable->tab + v;
list_for_each(cur, phtable->tab + v) {
if (phtable->hash_cmp(cur, node)) {
node_del_init(cur);
return 0;
}
}
return -1;
}
static int hash_push(struct hash_table *phtable, struct list_head *node)
{
assert(phtable != NULL);
assert(node != NULL);
size_t v = phtable->hash(node) % phtable->nmemb;
list_add_tail(node, phtable->tab + v);
return 0;
}
static struct list_head *hash_search(const struct hash_table *phtable, const struct list_head *key)
{
assert(phtable != NULL);
assert(key != NULL);
size_t v = phtable->hash(key) % phtable->nmemb;
static struct list_head *cur = NULL;
if (cur == NULL || !phtable->hash_cmp(key, cur)) {
cur = phtable->tab + v;
}
list_for_each_continue(cur, phtable->tab + v) {
if (phtable->hash_cmp(cur, key)) {
return cur;
}
}
return NULL;
}
int hash_table_init(struct hash_table *phtable, size_t nmemb,
size_t (*hash)(const struct list_head *node),
int (*hash_cmp)(const struct list_head *a, const struct list_head *b))
{
assert(phtable != NULL);
assert(hash != NULL);
assert(hash_cmp != NULL);
phtable->tab = (struct list_head *)malloc(sizeof(struct list_head) * nmemb);
assert(phtable->tab != NULL);
int i = 0;
for (; i < nmemb; i++) {
list_head_init(phtable->tab + i);
}
phtable->nmemb = nmemb;
phtable->hash = hash;
phtable->hash_cmp = hash_cmp;
phtable->search = hash_search;
phtable->push = hash_push;
phtable->del = hash_del;
return 0;
}
int hash_table_destroy(struct hash_table *phtable)
{
int i;
struct list_head *cur = NULL;
struct list_head *Next = NULL;
for (i = 0; i < phtable->nmemb; i++) {
list_for_each_safe(cur, Next, phtable->tab + i) {
node_del_init(cur);
}
}
free(phtable->tab);
return 0;
}
测试
#include <stdio.h>
#include "hash_table.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
struct data_info {
char *name;
char *tel;
struct list_head list;
};
size_t hash(const struct list_head *node)
{
struct data_info *pa = container_of(node, struct data_info, list);
return pa->name[0];
}
int hash_cmp(const struct list_head *a, const struct list_head *b)
{
struct data_info *pa = container_of(a, struct data_info, list);
struct data_info *pb = container_of(b, struct data_info, list);
return strcmp(pa->name, pb->name) == 0;
}
int main(void)
{
struct data_info s[] = {
{"tom", "137"},
{"mary", "135"},
{"candy", "155"},
{"jack", "189"},
{"richard", "131"},
};
struct hash_table *phtable = (struct hash_table *)malloc(sizeof(*phtable));
assert(phtable != NULL);
hash_table_init(phtable, 3, hash, hash_cmp);
int i = 0;
for(; i < sizeof(s) / sizeof(s[0]); i++) {
phtable->push(phtable, &s[i].list);
}
struct data_info person = {"jack"};
phtable->del(phtable, &(person.list));
struct list_head *pa = phtable->search(phtable, &(person.list));
if (pa == NULL) {
printf("not fount\n");
} else {
struct data_info *p = container_of(pa, struct data_info, list);
printf("%s %s\n", p->name, p->tel);
}
hash_table_destroy(phtable);
free(phtable);
return 0;
}