查找算法之哈希表
说道查找,我想大家并不陌生,所谓查找,即为根据某个给定的值,在查找表中确定一个其关键字等于给定值的记录或数据元素。
像比较常见的,有循序查找,针对有序表的有比较高效的二分(折半)查找,同时还有通过树来优化的静态树查找与动态树查找,但是这些无一不是在查找过程中要进行一系列的和关键字之间的比较。
那么,我们是否可以不需要比较就找到了目标呢? 当然,如果我们把相应查找表的存储位置(index)和其关键字之间构建一个确定的联系,那么在每一查找时,我们就可以直接通过关键字获得查找对象的存储位置,然后就可以拿到相应都目标对象了。
这就是哈希表所做的事儿!!!
下面举个栗子:
现需要建立一张全国30个地区的各名族人口统计表,每个地区有一个记录,记录的数据如下:
显然,使用数组存储,数组长度为30,每一个数组元保存一个地区的信息,一般情况下,我们所做的就是把地区一个一个的添加到数组里面去,最多也就是按照第一个汉字的拼音的开头字母进行一下排序,最终我们要去找某个元素的时候,虽然大概知道我们要找的元素大概在那个位置,但是是不是还是要进行相应的关键字比对,即使只有那么一两次(优秀的查找算法加上有序的查找表),哈希表所做的就是,我的关键字就是数组下标,(什么? –_– 你是不是疯了,我的关键字是中文,是字符串,怎么就和数组下标,不对 ),当然,关键字不能做下标,但是我们可以找到一种对应关系,是得每一个关键字都尽量对应唯一个下标(数值),这就是我们所说的哈希函数。
还是上面的例子:现列出三种关键字与存储位置(index)之间的对应关系:
第二种哈希函数:先求关键字的第一个和最后一个字母在字母表中的序号之和,和值若比30大,则减去30,
第三种哈希函数:先求每个汉字的第一个拼音字母的ASCII吗之和的八进制形式,然后将这个八进制数看成十进制数在除以30取余,若余数为零则加上30使其成为哈希函数值。
当然!从上面的表格中我们可以看到,不管值哪一种哈希函数,都存在通关关键字求出来的数组相同的情况,我们称之为冲突,从哈希函数的原理以及目的来分析,显然这是不可以的,如何解决,这里先买一个官子,先来谈谈如何构建哈希表,如何找到一个合适和哈希函数,何为合适,
如对于关键字集合的任意一个关键字,经哈希函数地址结合中任意一个地址的概率是相等的,则成此类哈希函数为均匀的哈希函数,即关键字的哈希地址均匀分布在整个地址空间中,从而减少冲突。
下面列出常用的构建哈希函数的方法:
1:直接定址法(取关键字和关键字的某个线性函数值为哈希地址)。
如:一个从1岁到100岁的人口数字统计表,其中,年龄作为关键字,哈希函数就可以取关键字本身,满足哈希表的要求。
2:数字分析法(通过关键字的整体情况来判断去关键字的某一段或者某一种形式):
原则:尽量选择关键字出现随机且相互独立的区段。
3:平方取中法(取关键字平方后的中间几位为哈希地址)
原理:一个数平方后的中间几位数和数的每一位都相关。
4:折叠法(将关键字分割为位数相同的几部分,然后取这几部分的叠加和,针对于关键字比较多的时候)
栗子:国际标准图书编号,0-442-20586-4 可以通过如下过程求得哈希函数值
5:除留余数法,取关键字 被 p(某个不大于哈希表表长m的的数) 除后所得余数的为哈希地址。
H(key) = key MOD p, ( p < m);
一般情况下,选择的p应应为质数(只能被1和其自身整除的数)。
6:随机数法
上面谈到在通过哈希函数求哈希值时,难免会遇见两个不同的关键字求得的哈希地址相同的情况,如何处理冲突?
处理冲突:为冲突的关键字记录找到另一个’空’的地址
1:开放定址法:
1):线性探测再散列: 即从冲突的地址开始往后找,直至找到‘空’的哈希地址。
2):二次探测再散列:一次取1的二次方,-1的二次方,2的二次方,-2的二次方……让当前冲突的哈希地址与这些数字一次相加知道找到‘空地址’,其实我们可以看做在冲突的地方两边波动并逐渐远离。
3):伪随机数序列(这个就顾名思义了)。
二次聚集:两个第一个哈希地址不同的记录争夺同一个后继哈希地址。 上图为例,我们已经求出了60,17,29的哈希地址,当我们就后面的关键字时,就有可能会求得其哈希地址为5,与关键字为60的有冲突,此时,若使用线性探测在散列,哈希地址将会变为8,这时,如果有一个关键字的算出来的哈希地址是6,与17冲突,这个新算出来的为6的关键字是不是本来也应该取8为其哈希地址的(根据线性探测再散列),此时五门就说这两个关键字(最新算出来的哈希地址与60相同的和最新算出来的哈希地址和17相同的)聚集。
2:再哈希法: H = R(H(key)):即在同义词产生地址冲突时通过另一个哈希函数计算哈希地址。
3:链地址法:创建另外一个链表,将所有关键字为同义词的记录存储在此线性表中。
4:建立公共溢出区:建立向量OverTable[0…v]:所有关键字和基本表中关键字为同义词的记录,不管其有哈希函数的到的哈希地址是什么,只要冲突发生都填入溢出表。