哈希表学习笔记1
什么是哈希表
顺序查找、折半查找、二叉排序树和B-树查找,这些查找方案的效率都依赖于查找过程中所进行的比较次数。
理想的情况是不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。因而在查找时,只要根据这个对应关系f找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。在此,我们称这个对应关系f为哈希函数,按这个思想建立的表为哈希表。
举个例子
假设有30个地区关键字,先列举5个,分别是北京,天津,河北,山西,上海。我们想要在某一次查找中快速找到我们想要的城市,那么我们先要构造哈希表,也就是怎么样把这五个城市按照一定次序放入这个哈希表。
举三个方案:
- 取关键字中第一个字母在字母表中的序号作为哈希函数。
- 先求关键字的第一个和最后一个字母在字母表中的序号之和,若比30(表长)大,则减去30。
- 先求每个汉字的第一个拼音字母的ASCⅡ码之和的八进制形式,然后将这个八进制数看成是十进制数再除以30取余数,若余数为0则加上30为哈希值。
key | BEIJING | TIANJIN | HEBEI | SHANXI | SHANGHAI |
---|---|---|---|---|---|
f1(key) | 02 | 20 | 08 | 19 | 19 |
f2(key) | 09 | 04 | 17 | 28 | 28 |
f3(key) | 04 | 26 | 02 | 13 | 23 |
可以看出:
- 哈希函数是一个映像,因此哈希函数的设定很灵活,只要使得任何关键字由此所得的哈希函数值都落在表长允许范围之内即可;
- 对不同的关键字可能得到同一哈希地址,这种现象称作冲突。具有相同函数值的关键字对该哈希函数来说是同义词。
然而,在一般情况下,冲突只能尽可能地少,而不能完全避免。因为哈希函数是从关键字集合到地址集合的映像。假设表长为n,则地址为0到n-1。例如,在C语言的编译程序中可对源程序中的标识符建立一张哈希表。
重新描述哈希表
根据给定的哈希函数H(key)和处理冲突的方法将一组关键字映射到一个有限的连续的地址集上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表称为哈希表,这一映像过程称为哈希造表或散列,所得存储位置称为哈希地址或散列地址。
哈希函数的构造方法
-
直接定址法:取关键字或关键字的某个线性函数值为哈希地址。
即:H(key)=key或H(key)=a*key+b
-
数字分析法
假设关键字是以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。
-
平方取中法
取关键字平方后中间几位为哈希地址。例如:
为Basic源程序中的标识符建立一个哈希表。假设Basic语言中允许的标识符为一个字母或一个字母和一个数字。在计算机中可用两位八进制数表示字母和数字。如下表:
A B C … Z 0 1 2 … 9 01 02 03 32 60 61 62 71 取标识符在计算机中的八进制数为它的关键字。假设表长为512= 2 9 2^9 29,则可取关键字平方后的中间9位二进制数为哈希地址。
-
折叠法
将关键字分割成位数相同的几部分,然后取这几部分的叠加和(舍去进位)作为哈希地址。
-
除留余数法
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。这是一种最简单也最常用的构造哈希函数方法。它不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。
H(key) = key MOD p , p ≤ \le ≤m
值得注意的是,在使用除留余数法时,对p的选择很重要。若p选的不好,容易产生同义词。
由众人的经验得知:一般情况下,可以选p为质数或者不包含小于20的质因数的合数。
-
随机数法
选择一个随机函数,取关键字的随即函数值为他的哈希地址,即H(key)=random(key)
处理冲突的方法
哈希函数可以减少冲突,但不能避免,因此,如何处理冲突是哈希造表不可缺少的另一方面。
在处理哈希地址的冲突时,若得到的另一个哈希地址 H 1 H_1 H1仍然发生冲突,则再求下一个地址 H 2 H_2 H2,若 H 2 H_2 H2仍然冲突,再求得 H 3 H_3 H3。以此类推,直至 H k H_k Hk不发生冲突为止,则 H k H_k Hk为记录在表中的地址。
通常用的处理冲突的方法有下列几种:
- 开放定址法
- 再哈希法
- 链地址法
- 建立一个公共溢出区