title: 数据结构与算法之哈希表(C语言版)
date: 2020-07-19 21:05:15
categories: 数据结构与算法
tags:
- 数据结构
- 算法
- 哈希表
- c
数据结构与算法之哈希表(C语言版)
哈希表支持一种最有效的检索方法:散列。
由于计算哈希值和在数组中进行索引都只消耗固定的时间,因此哈希表最大的亮点在于其是一种运行时间在常量级别的检索方法。
绝大多数的哈希函数会将一些不同的键映射到表中相同的槽位上,当两个键映射到一个相同的槽位上时,即产生了冲突。优秀的哈希函数能够最大程度的减少冲突,但是冲突是不可能完全消除的,需要设计者想办法处理冲突。
本文介绍两种类型的哈希表:
- 链式哈希表
- 开地址哈希表
链式哈希表
链式哈希表,从根本上来说是由一组链表组成,每个链表可以看成是一个“桶”。将所有的元素通过散列的方式放到具体的不同的“桶”中。
插入元素时,首先将键值传入一个哈希函数,哈希函数通过散列的方式告知元素属于哪个“桶”,然后在该“桶”相应的链表头部插入元素。
在查找或者删除元素时。用同样的方式先找到该元素存放的“桶”,然后遍历该“桶”对应的链表,直到发现想要查找的元素。
因为每个“桶”都是一个链表,因此链式哈希表并不限制包含元素的个数,但是随着元素越来越多,表变大,其性能将会降低。
解决冲突
当哈希表中的两个键散列到一个相同的槽位时,这两个键之间将会产生冲突。
链式哈希表解决冲突的方法:当冲突发生时,将元素放置到已经准备好的“桶”对应的链表头部位置。
当过多的冲突发生在同一个槽位时,此位置的“桶”将变得越来越深,从而造成访问该位置的元素所需的时间越来越多。
在理想的情况下,我们希望所有的“桶”以几乎相同的速度增长,这样他们就可以尽可能地保持小的容量和相同的大小。因此我们的目标是尽可能地均匀和随机地分配表中的元素,这种情况在理论上被称为均匀散列。
选择哈希函数
一个好的哈希函数,旨在近似均匀散列,即尽可能以均匀和随机的方式散布一个哈希表中的元素。
定义一个哈希函数h,其将键k映射到哈希表中的位置x,x被称为k的哈希码,表达式为:h(k)=x
一般来说,大部分的散列方法都假设k为整数,这样k能够很容易地以数学的方式修改。
可以使用取余法选择一个哈希函数:h(k) = k mod m
,m为槽位的数量
还可以使用乘法来选择一个哈希函数:h(k)=取整数部分(m(kA mod 1)) 其中A约等于0.618
接口定义
int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key),
int (*match)(const void *key1, const void *key2),
void (*destroy)(void *data));
/*
初始化参数htbl指定的链式哈希表
参数buckets指定哈希表中所分配的“桶”的个数
参数h为函数指针,指向用户自定义的哈希函数
参数match为函数指针,用于判断两个键是否匹配
参数destroy为函数指针,用来释放动态分配的内存空间
*/
void chtbl_destroy(CHTbl *htbl);
/*
销毁参数htbl所指定的链式哈希表
*/
int chtbl_insert(CHTbl *htbl, const void *data);
/*
向参数htbl指定的链式哈希表中插入一个元素
参数data指向新元素所包含的数据域
如果插入元素成功,返回0,如果哈希表中已经包含该元素,则返回1,否则返回-1
*/
int chtbl_remove(CHTbl *htbl, void **data);
/*
从参数htbl指定的链式哈希表中删除与data匹配的元素
函数返回时,参数data指向已经删除的元素的数据域
如果删除元素成功,返回0,否则返回-1
*/
int chtbl_lookup(const CHTbl *htbl, void **data);
/*
查找参数htbl指定的链式哈希表中是否有与data相匹配的元素
如果找到,则函数返回时,data将指向哈希表中相匹配元素的数据域
如果找到元素,则返回0,否则返回-1
*/
int chtbl_size(CHTbl *htbl);
/*
获取参数htbl指定的链式哈希表中元素的个数
*/
实现
链式哈希表包含一组“桶”,每个“桶”其实就是一个链表,链表用来存储散列到表中某些槽位的元素。
链式哈希表的抽象数据类型的头文件:
/* chtbl.h */
#ifndef CHTBL_H
#define CHTBL_H
#include <stdlib.h>
#include "list.h"
/* define a structure for chained hash tables */
typedef struct CHTbl_
{
int buckets; /* 桶的数量 */
int (*h)(const void *key); /* 哈希函数 */
int (*match)(const void *key1, const void *key2); /* 匹配函数 */
void (*destroy)(void *data); /* 销毁函数 */
int size; /* 哈希表中元素数量 */
List *table; /* 存储桶的数组 */
}CHTbl;
/* public interface */
int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key),
int (*match)(const void *key1, const void *key2),
void (*destroy)(void *data));
void chtbl_destroy(CHTbl *htbl);
int chtbl_insert(CHTbl *htbl, const void *data);
int chtbl_remove(CHTbl *htbl, void **data);
int chtbl_lookup(const CHTbl *htbl, void **data);
#define chtbl_size(htbl) ((htbl)->size)
#endif
链式哈希表的实现:
/* chtbl.c */
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "chtbl.h"
/* chtbl_init */
int chtbl_init(CHTbl *htbl, int buckets, int