代码改变世界

查找——图文翔解HashTree(哈希树)

2017-04-20 20:52  tlnshuju  阅读(3974)  评论(0编辑  收藏  举报


在各种数据结构(线性表、树等)中,记录在结构中的相对位置是随机的。因此在机构中查找记录的时须要进行一系列和keyword的比較。这一类的查找方法建立在“比較”的基础上。查找的效率依赖于查找过程中所进行的比較次数



之前我们介绍的各种基于比較的树查找算法,这些查找算法的效率都将随着数据记录数的增长而下降

不过有的比較慢(时间复杂度为O(n)),有的比較快(时间复杂度是O(logn))而已。这些查找算法的平均查找长度是在一种比較理想的情况下获得的。

在实际应用其中,对数据结构中数据的频繁添加和删除将不断地改变着数据的结构。

这些操作将可能导致某些数据结构退化为链表结构,那么其性能必定将下降。

为了避免出现这样的情况而採取的调整措施。又不可避免的添加了程序的复杂程度以及操作的额外时间。



哈希表 

理想的情况是希望不经过不论什么比較,一次存取便能得到所查的记录。那就必须在记的存储位置和它的keyword之间建立一个确定的相应关系f,使每一个keyword和一个唯一的存储位置相相应。因而在查找时。仅仅要依据这个相应关系f找到给定值K的像f(K)。由此,不须要进行比較便可直接取得所查记录。在此,我们称这个相应关系为哈希(Hash)函数。按这个思想建立的表为哈希表

在哈希表中对于不同的keyword可能得到同一哈希地址,这样的现象称做冲突。在普通情况下。冲突仅仅能尽可能地降低,而不能全然避免。由于哈希函数是从keyword集合到地址集合的映像。通常keyword的集合比較大,它的元素包含全部可能的keyword。而地址集合的元素仅为哈希表中的地址值。在普通情况下,哈希函数是一个压缩映像函数。这就不可避免的要产生冲突。



哈希树(HashTree)算法就是要提供一种在理论上和实际应用中均能有效地处理冲突的方法。一般的哈希(Hash)算法都是O(1)的,并且基本是以空间换时间。这非常easy导致对存储空间无限制的需求。

本文中哈希树(HashTree)算法在实际操作中使用了一些技巧使得对空间的需求控制在一定范围内。即空间需求仅和所须要存储的对象个数有关,不会无限制地“膨胀”下去。



哈希树的理论基础


质数分辨定理
简单地说就是:n个不同的质数能够“分辨”的连续整数的个数和他们的乘积相等

“分辨”就是指这些连续的整数不可能有全然同样的余数序列。


(这个定理的证明详见:http://wenku.baidu.com/view/16b2c7abd1f34693daef3e58.html

比如:
从2起的连续质数。连续10个质数就能够分辨大约M(10) =2*3*5*7*11*13*17*19*23*29= 6464693230 个数,已经超过计算机中经常使用整数(32bit)的表达范围。连续100个质数就能够分辨大约M(100) = 4.711930 乘以10的219次方。
而依照眼下的CPU水平,100次取余的整数除法操作差点儿不算什么难事。在实际应用中。总体的操作速度往往取决于节点将keyword装载内存的次数和时间。一般来说。装载的时间是由keyword的大小和硬件来决定的;在同样类型keyword和同样硬件条件下,实际的总体操作时间就主要取决于装载的次数。

他们之间是一个成正比的关系。



插入


我们选择质数分辨算法来建立一棵哈希树。
选择从2開始的连续质数来建立一个十层的哈希树。第一层结点为根结点。根结点下有2个结点。第二层的每一个结点下有3个结点。依此类推,即每层结点的子节点数目为连续的质数

到第十层,每一个结点下有29个结点。
同一结点中的子结点。从左到右代表不同的余数结果。


比如:第二层结点下有三个子节点。那么从左到右分别代表:除3余0,除3余1。除3余2.
对质数进行取余操作得到的余数决定了处理的路径



结点结构:结点的keyword(在整个树中是唯一的),结点的数据对象。结点是否被占领的标志位(标志位为真时,keyword才被觉得是有效的),和结点的子结点数组。


哈希树的节点结构

struct Node
{
    keyType      key ;
    ValueType    value ;
    bool         occupied ;    //用occupied来表示节点是否被占领。假设节点的keyword(key)有效。那么occupied应该设置位true,否则设置为false。

struct Node* subNodes[1] ; //我们用subNodes[i]来表示节点的第i个子节点的地址。(此技术在跳跃表中有介绍,可翻看前面博客) } ;

(假设在建立当初就建立全部的节点。那么所消耗的计算时间和磁盘空间是巨大的。

在实际使用其中,仅仅须要初始化根节点就能够開始工作。

子节点的建立是在有很多其它的数据进入到哈希树中的时候建立的。因此能够说哈希树和其它树一样是一个动态结构。)


以下我们以随机的10个数的插入为例,来图解HashTree的插入过程,这个史上最清晰的图解,你一定能看的明确^_^

有读者可能有疑问,假设一直冲突下去怎么办?首先,若keyword是整型。我们的10层哈希树全然能够分辨出来它们,这是质数分辨算法决定的。

(我们事实上也能够把全部的键-值节点放在哈希树的第10层叶节点处,这第10层的满节点数就包括了全部的整数个数。可是假设这样处理的话,全部的非叶子节点作为键-值节点的索引,这样使树结构庞大,浪费空间)

【这里没有说的太清楚,此图是以2開始的连续质数创建的,即:从上到下的层级中的每一个节点中的子树个数为2、3、5、7、11、13、17、19、23、29。第一层中的每一个节点的子树个数为2,第二层中的每一个节点子树个数为5.。。。。

上图中的子树上的数字。是其父节点的子树指针数组的索引值】


查找 

哈希树的节点查找过程和节点插入过程类似,就是对keyword用质数序列取余,依据余数确定下一节点的分叉路径,直到找到目标节点
如上图,最小”哈希树(HashTree)在从4G个对象中找出所匹配的对象,比較次数不超过10次。也就是说:最多属于O(10)。在实际应用中,调整了质数的范围,使得比較次数一般不超过5次。也就是说:最多属于O(5)。

因此能够依据自身须要在时间和空间上寻求一个平衡点。



删除 

哈希树的节点删除过程也非常easy。哈希树在删除的时候,并不做不论什么结构调整。
仅仅是先查到到要删除的节点,然后把此节点的“占位标记”置为false就可以(即表示此节点为空节点。但并不进行物理删除)。



长处

1、结构简单

从哈希树的结构来说。很的简单。每层节点的子节点个数为连续的质数。子节点能够随时创建。因此哈希树的结构是动态的,也不像某些哈希算法那样须要长时间的初始化过程。哈希树也没有必要为不存在的keyword提前分配空间。
须要注意的是哈希树是一个单向添加的结构,即随着所须要存储的数据量添加而增大。

即使数据量降低到原来的数量,可是哈希树的总节点数不会降低

这样做的目的是为了避免结构的调整带来的额外消耗。

2、查找迅速

从算法过程我们能够看出,对于整数,哈希树层级最多能添加到10。

因此最多仅仅须要十次取余和比較操作,就能够知道这个对象是否存在

这个在算法逻辑上决定了哈希树的优越性。
一般的树状结构。往往随着层次和层次中节点数的添加而导致很多其它的比較操作。操作次数能够说无法准确确定上限。而哈希树的查找次数和元素个数没有关系。假设元素的连续keyword总个数在计算机的整数(32bit)所能表达的最大范围内,那么比較次数就最多不会超过10次。通常低于这个数值。 

3、结构不变

从删除算法中能够看出,哈希树在删除的时候,并不做不论什么结构调整。这个也是它的一个很好的长处。常规树结构在添加元素和删除元素的时候都要做一定的结构调整,否则他们将可能退化为链表结构,而导致查找效率的减少。哈希树採取的是一种“见缝插针”的算法,从来不用操心退化的问题,也不必为优化结构而採取额外的操作。因此大大节约了操作时间。




缺点

1、非排序性

哈希树不支持排序。没有顺序特性。

假设在此基础上不做不论什么改进的话并试图通过遍历来实现排序,那么操作效率将远远低于其它类型的数据结构。



关于超长字符串的问题


假设是超长字符串的keyword。该怎样处理?若把它们按26进制每一位都转换为数字。则得到的结果太大。
我们能够用MD5等消息压缩算法来生成定长的整数。

关于MD5

维基链接:http://zh.wikipedia.org/wiki/MD5
MD5(Message Digest Algorithm 消息摘要算法第五版)
一种被广泛使用的password散列函数。能够产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

MD5算法具有下面特点:
1、压缩性:随意长度的数据。算出的MD5值长度都是固定的


2、easy计算:从原数据计算出MD5值非常easy。
3、抗改动性:对原数据进行不论什么改动,哪怕仅仅改动1个字节。所得到的MD5值都有非常大差别。


4、弱抗碰撞:已知原数据和其MD5值,想找到一个具有同样MD5值的数据(即伪造数据)是很困难的。
5、强抗碰撞:想找到两个不同的数据,使它们具有同样的MD5值,是很困难的。
(1996年后被证实存在弱点。能够被加以破解,对于须要高度安全性的数据。专家一般建议改用其它算法,如SHA-1)

对于超长字符串,我们能够用MD5算法生成一个128bit的整数。然后用RadixTree(翻看前面博客)来存储这个大整数。或者使用哈希树来存储,对于这种大整数。我们不能简单地使用计算机的整数来做除法,而是使用程序模拟人工的除法方式来做除法并获得余数。
这样,使用MD5和选用更大的质数相结合的办法。这样就能够使得通过层次比較少的哈希树来获得对keyword区间的完整覆盖。这样就降低了比較操作的次数,并提高总体的工作效率。



应用


哈希树能够广泛应用于那些须要对大容量数据进行高速匹配操作的地方

比如:数据库索引系统、短信息中的收条匹配、大量号码路由匹配、信息过滤匹配。

哈希树不须要额外的平衡和防止退化的操作。效率十分理想。


【參考】

http://baike.baidu.com/view/10403049.htm
http://wenku.baidu.com/view/16b2c7abd1f34693daef3e58.html

----------------------------------
感谢您的訪问,希望对您有所帮助。 欢迎大家关注、收藏以及评论。
----------------------------------