散列表的基本概念

来源:互联网 发布:天池大数据竞赛 案例 编辑:程序博客网 时间:2024/05/19 04:53

顺序查找、分块查找、折半查找及树形结构中的查找等,都是基于关键字的“比较”操作。散列查找是一种摆脱“比较”操作的查找方法。散列在关键字值及其存储位置之间建立某种关系,基于关键字值完成记录的访问。既包括数据元素的存储过程,也能实现数据元素的查找过程。

关键字值与其存储位置(称为哈希地址)之间的关系由散列函数(或哈希函数)给出,保存记录的查找表也称为散列表(哈希表)。采用散列函数在散列表中进行的查找方法称为散列方法(或哈希方法)。

1 哈希函数

哈希函数是一个映射关系,它将关键字映射到哈希地址,表示为:
其中addr是元素key在哈希表中的哈希地址。

影响哈希函数选择的因素包括:计算哈希函数所需的时间、关键字的长度、哈希表的大小、关键字的分布情况及记录的查找频率。

若两个关键字映射到同一哈希地址,称为“冲突”。将每个关键字映射到哈希表中唯一位置的哈希函数称为理想哈希函数(perfect hashing function)。理想哈希函数不会引起冲突,这种情况下,对表中元素都有常数的访问时间O(1)。
多数情况下可能找不到理想哈希函数,我们的目标是寻找一个好的哈希函数,它能合理地将元素散列到表中以尽量避免冲突。好的哈希函数仍能得到对哈希表的常数阶(O(1))访问时间。

对于一个具体的数据集,可用多种方法设计哈希函数。

(1)除留余数法

哈希函数为p是某个正整数。这个函数的结果在0到p-1之间。一般地,p取不大于表长的最大素数。

(2) 折叠方法

折叠方法(folding method)中,将关键字划分为几段,然后再将它们组合或折叠在一起得到哈希地址。首先,将关键字划分为多个子段,每个子段的长度与下标的长度一样,最后一段可能稍短。各段折叠的方式可以有多种变形。

在移位折叠方法(shift folding method)中,这些子段相加得到哈希地址。例如,如果关键字是社会保障码987-65-4321,可以将它们分为3段987,654和321。将它们相加得到1962。假定我们想要的是一个3位的哈希地址,此时可以再使用除留余数法或是抽取方法进行处理。

边界折叠(boundary folding)法也有多种变形。但一般来讲,先让关键字的若干子段反转然后再相加。例如:关键字的几个子段并排写在一张纸上,然后沿着关键字子段的边界把纸折叠起来。使用这种方法,仍以上述关键字987-65-4321为例,先把它划分为子段987,654和321。然后每隔一段反转其中的各位,得到987,456和321。相加这些子段得到1764,还是使用抽取方法或是除法方法得到下标。

折叠方法的其他变形是,使用不同的算法来决定反转关键字中的哪些部分。
当对字符串型关键字建立哈希函数时,也可使用折叠方法。例如,将字符串划分为与所需下标等长(按字节)的几个子串,然后使用异或函数将这几个子串组合起来。这个方法还可以将字符串转为一个数值,这样转换后,其他的方法例如除法方法也能用于字符串类型。

(3) 平方取中方法

在平方取中方法(mid-square method)中,关键字自乘,然后使用抽取方法从平方结果的中部抽取相应的位得到哈希地址。每次选择的“中部”各位必须相同,以保持一致性。例如,关键字是4321,自乘后得到18671041。假定我们需要的是3位关键字,则算法可以设计为抽取671或抽取710。也可以抽取二进制位而不是数位,然后从抽取的二进制位中构造下标。

将字符串中各字符按二进制格式进行处理,则平方取中方法也可以用于字符串类型。

(4) 基数转换方法

在基数转换方法(radix transformation method)中,关键字转换为另一种数值基数。例如,如果关键字是基数10下的23,可以将它转换为基数7下的32。然后可再结合应用除留余数法得到哈希地址。

(5) 数字分析方法

数字分析方法(digit analysis method)抽取关键字中的指定位并进行处理从而得到哈希地址。例如,如果关键字是1234567,可以选择2到4位,得到234,然后再处理得到哈希地址。处理方法可采用许多方式,包括反转各位(得到432),执行循环右移(得到423),执行循环左移(342),交换每对数位(324),或是许多其他的操作。

(6) 长度依赖的方法

在长度依赖方法(length-dependent method)中,关键字和关键字的长度以某些方式组合起来,或直接当作哈希地址使用,或再进一步使用其他方法进行处理得到哈希地址。例如,如果关键字是8765,可以将前两位乘上长度,然后再整除最后一位,得到69。如果表长是43,再使用除法方法,得到下标为26。

将字符串中各字符按二进制格式进行处理,则长度依赖方法也可以用于字符串类型。

2 解决冲突

如果能对具体的数据集找到一个理想哈希函数,就不必考虑冲突问题,所谓冲突即是多个元素或关键字映射到哈希表中的同一个位置。如果找不到理想哈希函数,或找到的哈希函数不实用(例如时间开销太大),则采用合理的哈希函数并加冲突解决机制。

有多个方法可以处理冲突。

(1) 链式方法

处理冲突的链式方法(chaining method),将哈希表看作是集合的表而不是各独立单元的表。哈希表的每个单元中保存一个指针,指向由所有映射到该地址的关键字组成的表。

使用该方法,最坏的情况是,哈希函数不能很好地将元素散列到表中,所以最终得到一个含n个元素的链表,或是几个含约n/k个元素的链表,这里k是个相对较小的常量值。这种情况下,哈希表的插入和查找都变为O(n)。

(2) 开放地址法

开放地址法(open addressing method)处理冲突的方法是,在表中寻找不同于该元素原先哈希到的另一个开放的位置,新的地址计算公式为:

其中, H ( key ) 是哈希函数, m 为哈希表长, di 是增量序列。

若di=1,2,3,…,m-1,称为线性探测再散列方法(linear probing),也称为线性探查;

若 di =1 2 , -1 2 , 2 2 , -2 2 , 3 2 , -3 2 , …,± k 2 ( k ≤ m /2 ) ,称为二次探测再散列方法(quadratic probing),也称为二次探查;

若用伪随机数序列当作增量序列di ,称为伪随机数再散列法,也称为伪随机数探查。

在线性探测方法中,如果一个元素被哈希到位置p,而位置p已经被占据了,则尝试位置(p+1)%s,其中s是表的大小。如果位置(p+1)%s也被占据,则再尝试(p+2)%s,依此类推,直到找到一个开放的位置,或是发现又回到最初的位置时为止。如果找到一个开放位置,则插入新元素。如果没找到一个开放的位置,表明哈希表已满。

线性探查方法的优点是在探查序列到达发生冲突的位置(基位置)之前,表中所有的位置都可以作为插入新记录的候选位置,但它的一次再探查有可能导致后续记录的冲突。这种把元素聚集到一起的倾向称为基本聚集。

二次探查方法和伪随机探查都能够消灭基本聚集,但如果两个关键字值散列到同一个基位置,它们就会具有同样的探查序列,这是因为探查序列只是基位置的函数,而不是原来关键字值的函数。如果散列函数在一个特定基位置导致聚集,那么在这两种探查下聚集仍会保持下来,这称为二级聚集。

0 0
原创粉丝点击