哈希表:Hash table

哈希表

1、什么是哈希表

      对于数组,我们可以通过下标直接进行访问,这种访问的速度很快,例如通过关键字key(1,2,...m)来定位其对应的元素data[1,2,...m]。假设可以用的内存很大而且关键字的全集(1,2,3,...m,...,2m)又不是很大,那么可以为每个元素分配一个内存空间,也就可以利用这种直接寻址的方法快速的访问元素。在直接寻址方式下,具有关键字key的元素被直接存放在索引为key的内存中。
      但在平常的使用过程中,常用的关键字(1,2,3,...m)只是其全集(1,2,3,...,m,...)的一部分且全集有时候非常大,如果为关键字全集赋予一个空间这大大浪费了资源甚至有时候是不可能的。当所使用的关键字集合K比起关键字域U要小很多,那么可以设计一种方式,将很”零散“分布在整个关键字域中通过一个转换,使其变得”紧致“一些。这个过程有点类似于空间变换,假想在一个矩形上的点沿着一边做投影,(假设投影不重合)只需要在边上搜搜就能找到对应的点而不是在整个平面中去找。或者可以想象用凸透镜聚光,聚光前和聚光后,光线所占的面积是不是变小了?! 因此,这种映射关系就变得很关键,它能将本来需要很多空间来构成的关键字表大大减小,这样经过映射之后的表称之为哈希表或者散列表。这种映射称之为散列函数或者哈希函数。

2、散列函数

散列函数可以表示为: h:U----〉{0,1,...,m-1}。其中我们所希望的关键字尽可能的在这个区间[0,m-1]上分布均匀,而且不论关键字是何种形式(数字或者字符)都希望映射后的值是自然数这样就能和散列表中数组的索引进行对应了。

1)直接地址法
取关键词的某个线性函数值为散列地址
h(key) = a*key+b

2)除法散列法
h(key) = key mod m
此种方法对m的选择是有要求的,一个不好的m很容易影响散列效果,一般不选择2的n次方,取素数。

3) 乘法散列法
h(key) = floor(m(key*A mod 1)) (0<A<1)
此种方法对m的选择没什么要求,一般选择2的n次方。

4)数字分析法
分析数字关键字在各个位上的变化情况,取比较随机的位作为散列地址。

......

3、碰撞问题

在之前的解释中,因为映射关系的存在,使得关键字变得”紧致“,但是如果两个不同的关键字经过映射后可能重叠,如矩形上的点沿着边投影很容易发生重叠,这就是所谓的碰撞。一个设计好的映射法则应该使得碰撞问题发生的情况尽可能少,但是关键字域所组成的表大于映射后的散列表,因此肯定有关键字经过映射后重叠(但这些关键字不一定会使用,即不是我们使用的关键字集),我们需要避免的是所使用的关键字集映射后发生碰撞。

1)一种解决碰撞的方案是使用链表,就是在关键字所对应的数组元素内存中存储一个链表的头指针。将拥有同一个映射值却不同关键字对应的元素存储于这个链表中。这时的散列表变为了一个一维数组,数组元素存储的指向一个链表的指针。

2)还有一种方式是开放寻址法(opening address)。其实就是寻找另一个地址存放对象,通过设计一个规则来计算新的索引,直至获得新的未被使用的空间存放地址。新的规则可以设计为:若发生h第i次冲突则hi(key)=(h(key)+di) mod m (0<i<m)。根据di的取值方式不同,这些新索引计算方法大致有:线性探索(di = i)、平方探索(di = + i^2 or -i^2)、双散列(di = i*h2(key),h2(key)= p - (key mod p))等。 线性探测会产生聚集效应,在冲突位置之后很容易再次产生冲突。平方探测有时候会找不到表中空闲的空间,但聚集效应不会太严重,如果m=4A+3(A为整数)形式的素数时平方探测法可以查找到整个散列表空间。 

再散列(rehashing):当散列元素太多时,装填因子太高使得查找困难,这是可以扩大哈希表,但需要将原先表中的元素重新散列。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值