1、序
该篇分别讲了散列表的引出、散列函数的设计、处理冲突的方法。并给出一段简单的示例代码。
2、散列表的引出
给定一个关键字集合U={0,1......m-1},总共有不大于m个元素。如果m不是很大,我们可以定义一个数组T[0...(m-1)],把U映射到数组T上,每个位置对应U中的一个关键字,若U中没有关键字为k的元素,则T[k]=NULL。我们称T为直接寻址表,不管是插入、删除、查找,只需o(1)的时间。但是注意前提,当”m不是很大的时候“。显然这个前提限制性很大,m很大时,必然会浪费很多空间,那该怎么办呢?于是就有了散列表:给定n个元素、m个存放位置(也称槽位),通过散列函数把关键字和存储位置关联起来,使每一个关键字与结构中一个唯一的存储位置相对应。于是在查找时,根据散列函数查找关键字的位置,不需要比较就可以取得要查找的记录。散列表也称哈希表。
3、散列函数
散列函数有很多,好的散列函数特点是:(近似的)满足简单一致散列,即对于关键字集合U中的任何一个关键字,经散列函数映射到地址集合中任何一个地址的概率是相等的,此时可以称为均匀散列函数,也就是说使关键字经过散列函数得到一个“随机的地址”,以便使一组关键字的散列地址均匀分布在整个地址区间,减少冲突。很多时候我们将关键字解释为自然数。下面给出几种常用的散列函数。
(1) 除法散列法
散列函数:h(key)=key%p,其中p的取值很重要,这个函数得出的散列地址值不会超过p,同时可以选作p的值常常是与2的整数幂不太接近的质数。当存放位置m较大时,p不宜过小。
(2)乘法散列法
散列函数h(key)=[p*(key*A-(int)key*A)].其中0<A<1.(key*A-(int)key*A)是取key*A得小数部分,最后再乘上常数p,最后的值向下取整。一般p选择2的某个幂次,对p的选择并没有什么特别的要求。
(3)全域散列
在执行开始时,从一族仔细设计的函数中,随机地选择一个作为散列函数。这里的随机选择针对的是一次对散列表的应用,而不是一次简单的插入或查找操作。散列函数的确定性,是查找操作正确执行的保证。全域散列法确保,当key1 != key2时,两者发生碰撞的概率不大于1/m。设计一个全域散列函数类的方法如下,该方法中,散列表大小m的大小是任意的。