散列hash算法与结构学习笔记

散列hash算法与结构学习笔记

一、概述

散列表(hash table)的实现一般叫散列(hashing)。散列是一种用于以常数平均时间执行插入、删除和查找的技术。但是,那些需要元素间任何排序信息的操作将不会得到有效的支持。所以诸如FindMin、FindMax以及以线性时间将排过序的整个表进行打印的操作都是散列所不支持的。即散列表ADT只支持插入、删除、查找和修改,这些是它的强项

二与树的比较

         从上面提到的散列表ADT可以看见,它只支持二叉查找树所允许的一部分操作,不支持元素间有任何的联系,所以树中很多ADT它不能实现。

三 散列表的基本原理

         理想的散列表数据结构只不过是一个包含有关键字的具有固定大小的数组,表的大小记做TableSize。每个关键字被映射到从0TableSize – 1这个范围中的某个数,并且被放到适当的单元中。这个映射就叫散列函数(hash function)。由于表单元数目有限,而关键字无限,所以总存在多个关键字映射到同一个表单元的情况,称冲突。所以寻找一个散列函数,使关键字均匀分布在单元之间很重要。实际上,选择映射函数确定散列表大小是使用散列算法的主要内容。

四hash算法

散列表的装填因子(load factor)为散列表中元素个数与散列表大小的比值。当这时找到一个简单均匀的散列函数很关键:

◆  简单指散列函数的计算简单快速

◆  均匀指对于关键字集合中的任一关键字,散列函数能以等概率将其映射到表空间的任何一个位置上。也就是说,散列函数能将子集K随机均匀地分布在表的地址集{0,1,…,m-1}上,以使冲突最小化。

基于此产生了很多算法,见资料用于查找的HASH算法 - aitao - 博客园.pdf。此外,

 

★  值得注意的是,散列函数按用途分可以分为两种,加密散列函数和非加密散列函数。非加密散列函数将字符串作为输入,通过计算输出一个整数。理想的散列函数的一个特性是输出非常均匀分布在可能的输出域,特别是当输入非常相似的时候。不同于加密散列函数,这些函数不是为防止攻击者找出碰撞而设计的,加密散列函数有这个特性但是要数,这些函数不是为防止攻击者找出碰撞而设计的,如MD5、SHA,这些可以在openssl源码中看到的lhash。加密散列函数有这个特性但是要慢的多。也就是说非加密散列函数追求的是均匀,用于查找;而加密散列函数追求的是单向性,没碰撞;

★  开源力量是强大的,对于非加密散列函数,也有很多很好的开源软件,诸如以下C实现中用到的lookup3,

其它的见Hash 函数概览 - 技术翻译 - 开源中国社区.pdf

五 解决冲突的办法

既然冲突不可避免,下面介绍几种常用的解决办法:

5.1 分离链接法(hash链表法)

         具体做法是将散列到同一个单元的所有元素保存到一个单向链表中。为方便起见,这些表都有表头(空间紧也可没有,没删除操作最好没有)。一般插入操作都是表头插入,越方便越好。常见的数据结构如下:

 Struct ListNode

  {

ElementTypeelement;

Struct ListNode*next;

}

 Struct HashTable

{

  int tableSize;

  Struct ListNode  * listArray;

}

具体实现见六。

 

分离链接散列算法的缺点是需要指针,给新单元分配地址需要时间,对算法的速度有些影响。同时算法实际上还要求对另一种数据结构的实现(链表)。但实际上,这种算法灵活,使用的最多。

5.2 开放定址法

在开放定址散列算法中,如果有冲突发生,那么就要尝试选择另外的单元,直到找出空的单元为止。因为所有的数据都要置入表内,而且装填因子,所以这种算法所需要的表要比分离链表散列表大,使用的空间也大。

开放定址法的这一要求本身也是很大的限制,一般都是用链地址法比较灵活。常用的开放定址法有线性探测法和平方探测法,这里不详述。

5.3 再散列

对5.2中的平方散列算法,如果表的元素添得太满,可建立另外一个大约两倍大的表(使用一个相关的新散列函数),扫描整个原始散列表,计算每个未删除的元素的新散列值并将其插入到新表中。这是一种非常昂贵的操作,不详述

5.4 可扩散列

这种主要处理数据量太大无法无法装入主存的情况。这时主要考虑的是检索数据所需的磁盘存取次数。与B-树相关,不详述

六 C的实现

基于开源的hash函数lookup3.c、使用分离链表算法(hash链表)的通用散列算法如下:

..\数据结构与算法分析:C语言描述(原书第2版)\源码\hash.c

..\数据结构与算法分析:C语言描述(原书第2版)\源码\opensrc\lookup3.c

..\数据结构与算法分析:C语言描述(原书第2版)\源码\include\hash.h

..\数据结构与算法分析:C语言描述(原书第2版)\源码\include\lookup3.h

七 小结

         散列表可用来以常数时间实现insert和Find操作。当使用散列表时,要注意装填因子这样的细节。对于分离链接散列法,虽装填因子不很大时性能并不明显降低,但装填因子还是应该接近于1。对于开放定址散列法,不应该超过0.5,否则性能会急剧下降。

         散列表与二叉查找树比较:二叉查找树也可以用来实现insert和find运算。虽然平均时间界为O(logN),但是二叉查找树也支持需要序的例程从而更强大。使用散列表不可能找出最小元素,除非准确知道一个字符串,否则散列表也不可能有效地查找它;二叉查找树还可以迅速找到一定范围内的所有项,这是散列表做不到的。但是,二叉树也不是万能的:有序的输入却可能使二叉树运行得很差,而平衡查找树的实现的代价相当高。因此,如果不需要有序的信息以及对输入是否被排序有怀疑,应该选择散列这种数据结构

你可能感兴趣的:(数据结构与算法)