实现来自算法精解:C语言描述(本人练习数据结构与基本算法记录,供回忆知识点使用)
代码上传至github:
https://github.com/G1enY0ung/DataStructure-and-Algorithm
哈希表(以下为我的理解,难免会有理解错误,欢迎大家指正)
1.介绍
哈希表在我看来是一种根据键(key)来访问到值(value)的数据结构。它的核心思想在于哈希函数的实现上,通过这个函数,我们可以便捷的通过键来得到我们想要的值。
比如说,我们要查找一个电话簿中某人的电话号码,就可以建立一个人名和姓氏首字母之间的关系,假设哈希函数为F(X),那么X就是人名,通过函数我们可以得到转换的hash value,也就是姓氏首字母。这样我们就可以不用挨个找电话号码了,直接通过姓氏首字母来查找,这样是不是感觉快速多了。
由于是通过键值来查找元素,我们只要构建好函数关系,查找的复杂度就成为了固定时间,通过key就可以查找到hash value。这就是哈希表的最大亮点。我们以链式哈希表来开始我们的哈希表构建之旅。^ _ ^
链式哈希表根本上来说由一组链表构成,每个链表可以看做一个桶(bucket),我们将所有的元素通过散列的方式放在不同的桶中。
××但是,当我们元素过多,插入表的值过多的话,性能将会大幅地降低。
2.冲突问题
如果我们有两个值,key1和key2,它们两个是互不相同的。但是通过F(key)之后,所得到的值变成相同的了,所以我们本来两个不同的数据却查找到同一个地方去了,这可咋整!
解决办法有好多种,其中的一种是通过取余法来实现,如果表有1699个位置,要散列的键为25657,那么哈希编码就可以为25657 % 1699 = 172。这样我们就可以尽量的避免冲突,将键均匀的分布起来。
具体的其他方法可以参见wiki
https://zh.wikipedia.org/wiki/%E5%93%88%E5%B8%8C%E8%A1%A8#.E5.A4.84.E7.90.86.E5.86.B2.E7.AA.81
3.定义如下(链式哈希表)
typedef struct Hash_
{
int buckets;
int (*h)(const void *key);
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
int size;
List *table;
} Hash;
int hash_init(Hash *htbl, int buckets, int (*h)(const void *key),
int (*match)(const void *key1, const void *key2),
void (*destroy)(void *data));
void hash_destory(Hash *htbl);
int hash_insert(Hash *htbl, const void *data);
int hash_remove(Hash *htbl, void **data);
int hash_lookup(const Hash *htbl, void **data);
#define hash_size(htbl) ((htbl)->size)
match还是匹配两个key之间是否相匹配。
h为哈希函数。
lookup是查找是否存在已有元素。
这里定义比较清晰了,具体实现可以参见github代码,也可参考算法精解这本书。
(更新)开地址哈希表
开地址哈希表我们就不用桶来标明数据的存放了,而是直接放在表本身中。这种实现对于固定大小的表很有用。
冲突解决
如何解决冲突也是我们要考虑的。我们用查找一个空的位置的方法来实现。
例如,如果我们要插入一个元素,探查整个表中的一个位置是空的槽位的话,就插入进去。删除或查找一个元素的时候,我们探查空位知道定位到该元素或者一个空的位置。如果在找到元素之前找到一个空位或者遍历完所有的空位,就说明该元素不存在。
探查函数定义为h(k, i)=x,其中k是键,i是到目前为止探查的次数,x是得到的哈希编码。
具体的探查方法流程在这里我就不画出了,参考算法精解这本书的图8-2,这本书的pdf网上有很多资源。
双散列
如果i的大小在0和m之间,那么哈希函数的定义为:
h(k, i) = (h1(k) + i * h2(k)) mod m
具体图解为8-3哈哈,比较懒,不传图了。
定义如下:
typedef struct OHASH_
{
int positions;
void *vacated;
int (*h1)(const void *key);
int (*h2)(const void *key);
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
int size;
void **table;
} OHASH;
int ohash_init(OHASH *htbl, int positions, int (*h1)(const void *key),
int (*h2)(const void *key),
int (*match)(const void *key1, const void *key2),
void (*destroy)(void *data));
void ohash_destroy(OHASH *htbl);
int ohash_insert(OHASH *htbl, const void *data);
int ohash_remove(OHASH *htbl, void **data);
int ohash_lookup(const OHASH *htbl, void **data);
#define ohash_size(htbl) ((htbl)->size)
具体实现参考github和算法精解这本书。