哈希表又叫散列表,根据关键字直接进行访问的数据结构,也就是说,它通过把关键字的值映射到表中某一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数,存放记录的数组叫做散列表或者叫哈希表。
很多人在第一次接触哈希表的时候一直弄不明白哈希表怎么能过快速定位到目标,其实关键就在于你在建立哈希表的时候就相当于对数据记录进行了有规律(这个规律就是散列函数了)的存入操作,而在进行查找时也是按你存入的规律去查找,这样自然就很快了。比如一个数组array[500]={1,4,2,5,3,,...},你在存入5的时候特意保存了5对应的下标3,这样你在要查找5的时候就没必要去遍历数组了,而是直接array[3]就成功取出了你想要的数据,所以说哈希表并没有你想象的那么神奇,它只不过是进行了有规律的存入,然后再按照一样的规律去检索而已。
学习哈希表的关键就在于理解哈希表的关键字、哈希值和散列函数,举个例子说,大家小时候都学过数学里面的函数概念,比如一次函数Y = k*X + b,X是自变量,Y是因变量,k和b参数,你在实际应用中,会考虑用什么来做自变量,然后对每个自变量的值因变量都有唯一一个值和它对应,就像数组中每一个下标都对应一个数据一样,其实哈希表和这个差不多,X就好比你选取的关键字,Y就是哈希值,而散列函数就是这个对应关系,它是从数据特征中抽象出来的表达式,哈希表的好坏很大程度上就取决于你所选取的散列函数,这也是哈希表设计的难点,而且在哈希表的设计中不可避免的会碰到冲突问题,如何去解决冲突这也是非常关键的问题,但散列函数的选取一直没有一个统一的标准,需要根据实际情况才能确定。
下面具体说一些常见的散列函数构造法。
1. 哈希表定义:存储位置 = f(关键字)
在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。对应关系f称为散列函数、哈希函数。
2. 散列函数的构造方法
2.1 直接定址法
直接定址就是取关键字的某个线性函数值为散列地址。
如:f(key) = a*key+b (a,b为常数)
适合:需要知道关键字分布情况,适合查找表较小且连续的情况
2.2 数字分析法
抽取关键字中一部分来计算散列位置
适合:关键字的位数比较大且若干位分布均匀
2.3 平方取中法
假设关键字1234,它的平方1522756,在取中间三位227作为散列地址
适合:不知道关键字的分布且位数不大
2.4 折叠法
比如关键字为0123456789,我们将其分为四组0123|4567|89,然后叠加求和0123+4567+89=4779,在取后四位4799作为散列地址。
适合:事先不知道关键字的分布,且关键字位数较多
2.5 随机数法
f(key) = random(key) // random是随机函数
适合:关键字长度不等的情况
2.6 除留余数法
f(key) = key mod p (p<=m)
适合:知道散列表的长度m
2.7 开放寻址法
H(key) = (f(key) + di) mod m
一旦发生冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法
di=1,2,3,…, m-1,称线性探测再散列
di=12, (-1)2, 22,(-2)2,…,±(k)^2,(k<=m/2)称二次探测再散列;
di=伪随机数序列,称伪随机探测再散列
2.8 再散列法
Hi=RHi(key), i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时
计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增 加了计算时间
2.9 链地址法(拉链法)
将产生冲突的元素链成一条链表,它是一种哈希表与遍历或二分查找等其它检 索方法相结合的一种方法
注:第一次写博客希望大家喜欢,在下一篇博客中我会写哈希表的一个例子,包括解决哈希冲突问题。