对哈希表的小见——再散列(嵌套)
来源:互联网 发布:我知女人心 结局 编辑:程序博客网 时间:2024/05/16 07:09
最近公司要开发一个快速查询和查看的模块,算法的复杂度不能与n值有关,也就是固定的、可预测的区间范围,即是这个搜索不以比较为依据进行。
快速索引,当然便想到哈希表(以前听到哈希表都惧怕,怕的就是冲突的问题),为了完成这个任务,还是只得选择在CSDN上搜索哈希表的相关知识。哈希表,是根据关键码值(Key value)而直接进行访问的数据结构(不了解的可以访问:http://baike.baidu.com/view/329976.htm?fr=aladdin、http://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;
以上就是最近在工作中得到的关于哈希表的经验。还是一句话:具体情况具体而定,以唯物观点看问题。
- 对哈希表的小见——再散列(嵌套)
- 对嵌套类的讨论
- 对嵌套映射的理解
- 我的一点小进步——ibatis isEmpty 和 iterate 嵌套查询
- Java学习笔记——关于for嵌套的几个小练习
- 我对record和嵌套表的测试—之五
- solr对嵌套(nesting)结构的支持
- Team Queue (优先对列的嵌套)
- android小知识——对图片的压缩方式
- 嵌套类(嵌套类、结构和枚举的作用域特征——图)
- 几个关于嵌套循环的小练习
- ListView嵌套ViewPager的小demo讲解
- 相关嵌套查询的一个小例子
- PLSQL跳出嵌套循环的小例子
- 对Java嵌套类的讨论
- Android小知识——ScrollView内的内部嵌套LinearLayout布局导致滑动条占位置
- 不一样的Javascript(10)——函数嵌套
- for两层的嵌套的应用(小正方形的生成,排成一排)
- UML初级学习之用例图
- 【翻译】两种高性能I/O设计模式(Reactor/Proactor)的比较
- 集群搭建必备:nat模式设置静态ip,达到上网与主机相互通信
- tp页面间传参注意事项
- 浅析vs2008中 Run-Time Check Failure #2 - Stack around the variable 'var' was corrupted
- 对哈希表的小见——再散列(嵌套)
- Flex组件开发阶段小结
- WordPress中常用动作钩子函数
- iOS开发之UITableView全面解析
- HDU 1032 The 3n + 1 problem【递归】
- 优化UITableView性能
- Prelude:如何成为一个优秀的软件工程师?
- 的说法你赶快
- 集群搭建必备:虚拟机之一实现Host-only方式上网