散列表概述

来源:互联网 发布:java 导出word 加水印 编辑:程序博客网 时间:2024/05/19 04:50

字典是形如(k,v)元素的集合,其中k是一个关键字,v是与k有关的值。字典中没有两个元素具有相同的关键字。
在字典上可以执行下面的操作
1)从字典中获取具有指定关键字的元素。
2)在字典中插入或放入具有指定关键字的元素
3)删除或移除具有指定关键字的元素

public interface Dictionary{   public Object get(Object key);   public Object put(Object key, Object theElement);   public Object remove(Object key);}

字典表示的一种方法是使用散列表,java提供了几个散列表的实现——java.util.HashTable、java.util.HashMap、java.util.HashSet

散列的概念属于查找,它不以关键字的比较为基本操作,采用直接寻址技术。在理想情况下,查找的期望时间为O(1)。
这里写图片描述

在以下的讨论中,我们假设处理的是值为整型的关键码,否则我们总可以建立一种关键码与正整数之间的一一对应关系,从而把该关键码的检索转化为对与其对应的正整数的检索;同时,进一步假定散列函数的值落在0到M-1之间。
散列函数的选取原则是:
1、运算尽可能简单;
2、尽可能使得结点均匀分布,也就是尽量让不同的关键码具有不同的散列函数值。

1、散列函数构造
1)直接定址法
取关键字或关键字的某个线性函数值为哈希地址:h(x)=x或 h(x)=a·x+b,其中a和b为常数,这种哈希函数叫做自身函数。
例如:100个学生的学号落在95100和95200范围中。函数 h(x) = x-95100把学号映射到表中的0至100位置

由于直接定址所得地址集合和关键字集合的大小相同。因此,对于不同的关键字不会发生冲突。但实际中能使用这种哈希函数的情况很少。

此法仅适合于:地址集合的大小 = = 关键字集合的大小,其中a和b为常数。
2)数字分析法
When the elements that are going to be in the hash table are known in advance, we can analyze the keys and select a subset of the digits to form the home address. The subset is obtained by eliminating those digits whose values are most skewed and hence are less useful in spreading the elements uniformly across the space of bucket addresses.
假设关键字集合中的每个关键字都是由 s 位数字组成 (u1, u2, …, us),分析关键字集中的全体,并从中提取分布均匀的若干位或它们的组合作为地址。
此法适于:能预先估计出全体关键字的每一位上各种数字出现的频度。
3)除留余数法
顾名思义,除余法就是用关键码x除以M(往往取散列表长度),并取余数作为散列地址。除余法几乎是最简单的散列方法,散列函数为: h(x) = x mod M。
这是一种最简单,也最常用的构造哈希函数的方法。它不仅可以对关键字直接取模(MOD),也可在折迭、平方取中等运算之后取模。值得注意的是,在使用除留余数法时,对p的选择很重要。一般情况下可以选p为质数或不包含小于20的质因素的合数。
4)乘余取整法
f(k) = floor(m * FractionalPart(k*A))
k:关键字
A:常数,Donald Knuth’s book The art of computer programming::Sorting and searching, volume 3, Addison-Wesley, 1973 suggests using A = (sqrt(5) - 1)/2 = 0.6180339887.
m:常数,The range of the hash function f is the integers 0, 1, 2, … m-1.
FractionalPart():取数值的小数部分

比如:假设k=100,A = 0.6180339887,和m = 20 则计算如下:

f(k) = floor(20 * FractionalPart(100 * 0.6180339887)) = floor(20 * FractionalPart(61.80339887)) = floor(20 * 0.80339887) = floor(16.0679774) = 16 .

该方法最大的优点是m的选取比乘余取整法要求更低。比如,完全可选择它是2的整数次幂。

5)平法取中法
由于整数相除的运行速度通常比相乘要慢,所以有意识地避免使用除余法运算可以提高散列算法的运行时间。平方取中法的具体实现是:先通过求关键码的平方值,从而扩大相近数的差别,然后根据表长度取中间的几位数(往往取二进制的比特位)作为散列函数值。因为一个乘积的中间几位数与乘数的每一数位都相关,所以由此产生的散列地址较为均匀。
In this the key k is squared and the middle p bits used as the home address. Here p is a constant and the hash function range is the integers 这里写图片描述.
举例如下:
例:将一组关键字(4,6,10,9,7)
平方后得(16,36,100,81,49)
转变为二进制(00010000,00100100,01100100,01010001,00110001)
若取表长为20,则可取中间的四位数作为散列地址集:(0100,1001,1001,0100,1100)。
转变成10进制(4,9,9,4,12)

6)折叠法
有时关键码所含的位数很多,采用平方取中法计算太复杂,则可将关键码分割成位数相同的几部分(最后一部分的位数可以不同) ,把这些部分的数据叠加起来,就可以得到具有该关键码的记录的散列地址。有两种叠加方法:
移位法 — 把各部分的最后一位对齐相加;
分界法 — 各部分不折断,沿各部分的分界来回折叠,然后对齐相加,将相加的结果当做散列地址。
一般当关键码的位数很多,而且关键码每一位上数字的分布大致比较均匀时,可用这种方法得到散列地址。
这里写图片描述
把超出地址位数的最高位删去, 仅保留最低的3位,做为可用的散列地址。

此法适于:关键字的数字位数特别多。

7)随机数法
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)=random (key),其中random为随机函数。通常,当关键字长度不等时采用此法构造哈希函数较恰当。

处理散列冲突的方法
可以参考这篇文章http://www.nowamagic.net/academy/detail/3008050
1)开发地址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
线性探测法,二次探测法,随机探测法
为产生冲突的关键字地址 H(key) 求得一个地址序列: H0, H1, H2, …, Hs 1≤s≤m-1,Hi = ( H(key) +di ) MOD m,其中: i=1, 2, …, s,H(key)为哈希函数;m为哈希表长;di为增量序列;
(1)冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。
di=1,2,3,…,m-1,称线性探测法;
(2)冲突发生时,在表的左右进行跳跃式探测,双向寻找到可能的空位置,增加平方运算的目的是为了不让关键字都聚集在某一块区域,减少“二次聚集”现象。
di=1,-1,2,-2,4,-4,9,-9,16,-16,…k*k,-k*k(k<=m/2)称为二次探测法或平方探测法(Quadratic Probing);
(3)在冲突时,对于位移量 di 采用随机函数计算得到,我们称之为随机探测法。
di=伪随机数序列,称伪随机探测法。
线性探测再散列容易产生“二次聚集”,即在处理同义词的冲突时又导致非同义词的冲突。
线性探测再散列的优点是:只要哈希表不满,就一定能找到一个不冲突的哈希地址,而二次探测再散列和伪随机探测再散列则不一定。
2)链地址法
这里写图片描述
将所有哈希地址相同的记录都链接在同一链表中。
3)再散列函数法
方法:构造若干个哈希函数,当发生冲突时,根据另一个哈希函数计算下一个哈希地址,直到冲突不再发生。即:Hi=Rhi(key) i=1,2,……k,其中:Rhi——不同的哈希函数,特点:计算时间增加

0 0
原创粉丝点击