之前的查找算法,时间复杂度为O(n),或者O(㏒2n),其效率取决于“比较”的次数。
即使对于采取排序树结构的查找表,由于每一次比较的结果,如果关键字与数据元素不相等,则有“大于”或者“小于”两个结果,所以下一步会有两种可能的方向,因此O(㏒2n)已经是最优了。
哈希表(Hash Table)采取另一种算法,其查找的时间复杂度最快可以达到O(1),即只要给出关键字,立刻就可以查找到该元素。
实际应用中大量采取哈希表的方式,很多编程语言中内置了哈希查找的方式,如java中的HashTable,HashSet等。
基本思想
将记录的存储地址和它的关键字之间建立一个确定的对应关系;这样,不经过比较,一次存取就能得到所查元素的查找方法。
1. 哈希函数(Hash函数)
f : 关键字 → 存储位置。即,hash函数是指关键字与存储位置(哈希地址)的对应关系,只要给出关键字,就可以通过这个函数得到存储位置。
如:给定一个保存了10个元素的数组
[ 0 1 2 3 4 5 6 7 8 9 ]
如果给定Hash函数f(K)=K mod 10,就可以立刻得到该键值对应元素的数组下标
如果给定Hash函数f(K)=K mod 2,则虽然也缩小了查找范围,但达不到上面函数的效果
如果不同的键值,被Hash函数映射到同样的一个哈希地址,则称为冲突(ki?kj,H(ki)=H(kj))
同义词:具有相同函数值的两个不同的关键字,称为该哈希函数的同义词。
Hash函数不可能完全避免冲突,只可能尽量减少冲突,或者说,好的Hash函数能将关键字映射后得到的哈希地址,尽量均匀地分布
设计Hash表的时候,除了采用好的Hash函数之外,如何有效地处理冲突,也是很重要的一方面
哈希表:
根据设定的哈希函数H(key)和处理冲突的方法,将一组关键字映射到一个有限的连续的地址区间上,并以关键字的映像作为记录在表中的存储位置。
映射过程称为哈希造表,或者散列(哈希表有时也叫散列表)
所得的存储位置值称为哈希地址或者散列地址
哈希函数是一种映象,其设定很灵活,只要使任何关键字的哈希函数值都落在表长允许的范围之内即可。哈希函数“好坏”的主要评价因素有:
◆ 散列函数的构造简单;
◆ 关键字经哈希函数映射后,得到的地址是等概率的,即“均匀”的,这样的Hash函数称为“均匀的哈希函数”
好的Hash函数,可以减少关键字的冲突。
直接定址法:取关键字或者关键字的某个线性函数作为Hash地址,即:
H(key) = key
H(key) = a×key + b,a、b为常数
特点:直接定址法所得地址集合与关键字集合大小相等,不会发生冲突,但实际中很少使用,因为需事先知道关键字的分布情况。
数字分析法:取关键字的若干位或组合作为哈希地址
平方取中法:将关键字平方后取中间几位作为哈希地址
折叠法:将关键字分割成位数相同的几部分(最后一部分可以不同),然后取这几部分的叠加和作为哈希地址
除留余数法(取模)
把关键字对某个数p(不大于哈希表的表长m)取模,所得到的数作为哈希地址
H(key) = key MOD p, p≤m
利用这种方法的关键是p的选取,p选的不好,容易产生同义词。
根据经验:若散列表表长为m,通常p为小于或等于表长(最好接近表长m)的 质数;或不包含小于20质因子的合数(如:23*29)。
随机数法
H(key) = random(key),把产生的随机数做为哈希地址。
当散列表中关键字长度不等时,该方法比较合适。
总结:根据具体问题,采取不同的哈希函数,依据以下:
哈希函数计算时间
关键字长度
哈希表大小(哈希地址范围)
关键字分布情况
记录查找频率
冲突:由关键字得到的哈希地址的位置上已经有记录
哈希函数只能使哈希地址均匀分布,但不能避免冲突,因此冲突处理是不可避免的
处理冲突的基本思想:在处理的过程中,可能会得到一个地址序列Hi,i = 1,2,…,k,即每次得到一个哈希地址Hi,若仍然发生了冲突,则再由相应方法得到下一个哈希地址Hi+1,直到得到一个不发生冲突的哈希地址为止
冲突处理即找到这个相应方法,即当出现冲突时,为冲突元素找到另一个存储位置。
开放地址法
处理冲突函数为
Hi = (H(key) + di) MOD m,
i = 1,2, …,k, k ≤ m-1
Hi为哈希函数,m为哈希表的表长,di为增量序列
di的选取方法
di=1,2,3, …,m-1,称为“线性探测再散列”
di=12,-12,22,-22, …,k2,-k2, k≤m/2,称为“二次探测再散列”
di为伪随机数序列,称为“伪随机探测再散列”
构造若干个哈希函数,当发生冲突时,利用不同的哈希函数再计算下一个新哈希地址,直到不发生冲突为止。即:Hi=RHi(key) i=1, 2, …, k
RHi :一组不同的哈希函数。第一次发生冲突时,用RH1计算,第二次发生冲突时,用RH2计算…依此类推知道得到某个Hi不再冲突为止。
◆ 优点:不易产生冲突的“聚集”现象;
◆ 缺点:计算时间增加。
方法:将所有关键字为同义词(散列地址相同)的记录存储在一个单链表中,并用一维数组存放链表的头指针。
设散列表长为m,定义一个一维指针数组:
RecNode *linkhash[m],
其中RecNode是结点类型,每个分量的初值为空。
凡散列地址为k的记录都插入到以linkhash[k]为头指针的链表中。
哈希表的查找过程与构造过程基本一致,在查找过程中,利用哈希函数和冲突函数,直到查找失败或者查找成功。
从哈希查找过程可见:
散列表在关键字与记录的存储地址之间建立了直接映象;
由于“冲突”,哈希表也存在着关键字的比较,其性能瓶颈取决于比较的次数
评价哈希查找效率仍要用ASL(平均查找长度)
哈希函数
哈希函数的好坏,影响出现冲突的频繁程度。一个均匀的哈希函数,对一组关键字,产生的冲突可能性都相同,它不是影响ASL的决定性因素
冲突处理方法
针对所介绍的几个冲突处理方法(线性探测、二次探测、随机探测、再探测、链地址),各自的ASL不同
装填因子(衡量哈希表的装满程度)影响该哈希表的ASL
? = 表中记录数/哈希表长度
装填因子越小,发生冲突的可能性就越小,反之就越大(需比较的次数就越多)
结论:哈希表的平均查找长度(ASL)跟装填因子有关,而跟记录数无关
哈希函数与冲突处理函数的设计,是哈希表设计的两个核心问题,如果设计得当,哈希表由于其他查找表,这也是它被广泛使用的原因
原文地址:http://blog.csdn.net/wangzi11322/article/details/45457031