1、哈希表
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
映射函数叫做散列函数,存放记录的数组叫做散列表。
冲突问题:两个不同的键映射到同一个位置
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
2、链式哈希表
数组每个元素存放一个链表首地址,一个链表为一个桶。输入一个数据,函数h将其转化为整型数据k,根据k处理得到数组元素指向的桶,在桶(链表)中执行插入或删除操作。
对于一定数量的元素,桶的数量m决定每个桶的大概深度。桶少要存储所有数据则桶的深度大。若使用取余法作为哈希函数,那么m一般取不为2的幂的素数。
3、代码分析
(1)数据结构
//CHTbl是哈希表的整体属性
typedef struct _CHTbl{
int size;//存入的数据个数
int buckets;//桶数
int (*h)(const void *data);//哈希函数,转换索引值
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
LIST_ATTRITIVE *table;//数组首地址,数组中每个元素是一个链表(桶)的整体属性
}CHTbl;
typedef struct _LIST_ATTRITIVE
{
int size;
void (*destroy)(void (*data));
LIST_ELEMENT *head;
LIST_ELEMENT *tail;
}LIST_ATTRITIVE;
typedef struct _LIST_ELEMENT
{
void *data;
struct _LIST_ELEMENT *next;
}LIST_ELEMENT;
(2)链式哈希表初始化:初始化CHTbl,为每个一级链表元素LIST_ATTRITIVE分配空间,CHTbl中数组table指向每个一级链表元素。
#include <stdio.h>
#include <string.h>
#include "chtbl.h"
#include "../SingleLink/single_list.h"
//确定桶数,初始化每个桶的链表,
int chtbl_init(CHTbl *htbl, int bucket, int (*h)(const void *key),
int (*match)(const void *key1, const void *key2), void (*destroy)(void *data))
{
int i;
//为数组每个元素指向的桶属性分配空间 ,该空间长期有效
if((htbl->table = (LIST_ATTRITIVE *)(malloc(bucket * sizeof(LIST_ATTRITIVE)))) == NULL)
return -1;
htbl->buckets = bucket;
for(i = 0; i < bucket; i++)
{
list_init(&htbl->table[i], 0 , destroy, NULL, NULL);
}
htbl->h = h;
htbl->match = match;
htbl->destroy = destroy;
htbl->size = 0;
return 0;
}
void chtbl_destroy(CHTbl *htbl)
{
int i;
for(i = 0; i < htbl->buckets; i++)
{
list_destroy(&htbl->table[i]);
}
free(htbl->table);
memset(htbl, 0 sizeof(CHTbl));
return;
}
//插入元素,只需要找到桶,在链表尾部插入
int chtbl_insert(CHTbl *htbl, const void *data)
{
void *temp;
int bucket, retval;
temp = (void *)data;
if(chtbl_lookup(htbl, &temp) == 0)
return 1;
//处理data得到该放入哪个桶
bucket = htbl->h(data) % htbl->buckets;
//在该桶指向的链表的尾部插入
if((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
htbl->size++;
return retval;
}
//删除元素, 要便利对应桶的链表找到该元素
int chtbl_remove(CHTbl *htbl, void **data)
{
LIST_ELEMENT *element, *prev;
int bucket;
bucket = htbl->h(*data) % htbl->buckets;
prev = NULL;
//遍历 桶指向的链表
for(element = htbl->table[bucket]->head; element != NULL; element = element->next)
{
if(htbl->match(*data, element->data))
{
if(list_rem_next(&htbl->table[bucket], prev, data) == 0)
{
htbl->size--;
return 0;
}
else
{
return -1;
}
}
//prev是匹配之间那个为匹配的元素
prev = element;
}
return -1;
}
int chtbl_lookup(CHTbl *htbl, void **data)
{
LIST_ELEMENT *element;
int bucket;
bucket = htbl->h(*data) % htbl->buckets;
for(element = htbl->table[bucket]->head; element != NULL; element = element->next)
{
if(htbl->match(*data, element->data))
{
*data = element->data;
return 0;
}
}
return -1;
}