对哈希表的小见——再散列(嵌套)

来源:互联网 发布:我知女人心 结局 编辑:程序博客网 时间:2024/05/16 07:09

最近公司要开发一个快速查询和查看的模块,算法的复杂度不能与n值有关,也就是固定的、可预测的区间范围,即是这个搜索不以比较为依据进行。

快速索引,当然便想到哈希表(以前听到哈希表都惧怕,怕的就是冲突的问题),为了完成这个任务,还是只得选择在CSDN上搜索哈希表的相关知识。哈希表是根据关键码值(Key value)而直接进行访问的数据结构(不了解的可以访问:http://baike.baidu.com/view/329976.htm?fr=aladdinhttp://zh.wikipedia.org/wiki/%E5%93%88%E5%B8%8C%E8%A1%A8。解决冲突的方法有:开放寻址法、再散列法、链地址法、建立公共缓冲区。以下解决冲突的四种方法:

链地址法:将所有关键字为同义词的记录存储在同一线性链表中。

 

开放寻址法:Hi=(H(key)+di)MOD m i=1,2,...,k(k<=m-1),其中m为表长,di为增量序列。如果di值可能为1,2,3,...m-1,称线性探测再散列;如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2) 称二次探测再散列;如果di取值可能为伪随机数列,称伪随机探测再散列。例:在长度为11的哈希表中已填有关键字分别为17,60,29的记录,现有第四个记录,其关键字为38,由哈希函数得到地址为5,若用线性探测再散列,如下:

 

再哈希法:当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。

建立公共溢出区:假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。

这里最简单,最常用的应该是链地址法,教程举例都是这样。

 

再说项目,该功能是使用大小为13个字节的数据作为关键字构建哈希表。13个字节,好大的数字2^104 = 1.0141204801825835e+31(^表示幂)呢。采用链地址法?网上也只能搜到这样的实例。这样的话,在解决一次冲突之后使用了链表,当然不合符要求。而且这个解决一次冲突的映射关系也是个非常苦恼的事情。这个想法在头脑里固定了一段时间,想想实际要处理的数据情形——同一时间段重复的数据比较多。那能不能细分?能。

一个字节最大表示的个数是255,将关键字拆分按字节计算,就用这个256数据作为关键字构建哈希表。数据只在0-255之间,那么我的第一个冲突就很好地解决了。哈希表的成员所指向的,如果是另一张子表,子表也0-255之间;只是最后一张表是尾表,里面的表项不是子表,而是数据。这样,由于关键字的长度固定,数据都在最后一层表里。这样构成了一棵很好的哈希树。树结构图如下:

(上图表示的是按5个字节的关键字存储的数据,关键字分别为2 4 9 255 4[0x020409FF04]、2 255 7 69[0x02FF070609]、9 7 8 4 255[0x09070804FF],0x表示十六进制)

当然这些表是动态建立的,如果数据来得很零散,就会造成哈希表所开僻的空间越大。对于13字节的关键来说,开发的平台上是128GB的内存,表开满了所占内存相当大(2^(13 * 8)、2^104字节,2^74 GB,注:1G=2^30),128GB内存也是海之一粟。所以,该算法只在数据关键字比较集中的时候可用。

 

测试:平台为64位128GB内存的linux系统,开启20GB表缓存[哈希表预先分配空间,未加入哈希树],关键字递增的方法进行,结果是0.022秒/百万条(0.00144秒/65536条) 插入数据速率,搜索速率更快,因为搜索过程少了分配空间的操作。


还有,在这个问题中,也存在表缓存不够用的情况,加入删除表机制和缓存管理机制最好,删除表机制用于释放哈希树中需要释放的树节点,缓存管理表用来管理每一块标记为释放的表(在释放的空间上连接这些释放管理表,那就没有多余的内存消耗啦)。

该哈希表在C代码实现数据结构如下:

<pre name="code" class="cpp">// 一个字节表示的最大个数#define BIT_EXPRESS_MAXVALUE   (256)// 哈希表数据结构typedef struct s_hashtable{    union     {        // 中间表数据类型        struct <span style="font-family: Arial, Helvetica, sans-serif;">s_hashtable *child</span><span style="font-family: Arial, Helvetica, sans-serif;">[BIT_EXPRESS_MAXVALUE];</span>        // 尾表数据类型        unsigned char *data[BIT_EXPRESS_MAXVALUE];    }t;}SHashTree;// 非递归遍历使用的栈结构typedef struct s_stack{    // 入栈的哈希表指针    SHashTree *table;    // 入栈时指定的哈希表成员索引    int index;}SStack;


以上就是最近在工作中得到的关于哈希表的经验。还是一句话:具体情况具体而定,以唯物观点看问题。

0 0
原创粉丝点击