数据结构与算法:hashtable

实现来自算法精解: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和算法精解这本书。

    原文作者:G1enY0ung
    原文地址: https://www.jianshu.com/p/2ff96b2e20fb
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞