STL源码剖析-hashtable

hashtable(散列表)结构,在插入、删除、搜索等操作也具有”常数平均时间”的表现。


hashtable概述

举个例子,假设所有元素都是16bits,范围0~65535,简单的使用一个array即可以满足常数时间的插入、删除、搜索。

  1. 创建数组array A,拥有65536个元素,索引号码0~65535,初始值全部为0

  2. 当插入元素i就执行A[i]++,删除元素就执行A[i]–,如果搜索元素i就检查A[i]是否为0
    下图为插入了元素:5,8, 3, 8, 58, 65535, 65534 数组内容。

这里写图片描述

但是上面的解法存在两个问题:

  1. 如果元素是32-bits而非16-bits那么所需的array A的大小就是232=4GB,这么大就不切实际了。

  2. 如果元素是字符串而非整数,就无法拿来做array的索引了。

第二个问题不难解决:可以将字符编码,每个字符以7-bits的数值表示(也就是ASCII码),如字符串”jjhou”表现为:

这里写图片描述

数值太大了,这有回到了问题一。

那么,如何避免一个大的慌缪的array呢?办法之一就是使用某种映射函数(hash function散列函数),将任意的元素映射到TableSize范围之内。

二、常用的哈希函数
1. 直接寻址法
取关键字或者关键字的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这种散列函数也叫做自身函数.如果H(Key)的哈希地址上已经有值了,那么就往下一个位置找,知道找到H(Key)的位置没有值了就把元素放进去.

  1. 数字分析法
    分析一组数据,比如一组员工的出生年月,这时我们发现出生年月的前几位数字一般都相同,因此,出现冲突的概率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果利用后面的几位数字来构造散列地址,则冲突的几率则会明显降低.因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址.

  2. 平方取中法
    取关键字平方后的中间几位作为散列地址.一个数的平方值的中间几位和数的每一位都有关。因此,有平方取中法得到的哈希地址同关键字的每一位都有关,是的哈希地址具有较好的分散性。

  3. 折叠法
    折叠法即将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和…….

  4. 除留余数法
    取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址.

使用hash function会带来一个问题:不同元素可能会被映射到相同的位置。这便是所谓的“碰撞(collision)”问题。解决的办法有很多种,包括线性探测(linear probing),二次探测(quadratic probing),开链(seperate chaining)…等做法。

哈希冲突的处理方法

  • 开放定址法——线性探测

线性探测法的地址增量di = 1, 2, … , m-1,其中,i为探测次数。该方法一次探测下一个地址,知道有空的地址后插入,若整个空间都找不到空余的地址,则产生溢出。
线性探测容易产生“聚集”现象。当表中的第i、i+1、i+2的位置上已经存储某些关键字,则下一次哈希地址为i、i+1、i+2、i+3的关键字都将企图填入到i+3的位置上,这种多个哈希地址不同的关键字争夺同一个后继哈希地址的现象称为“聚集”。聚集对查找效率有很大影响。

  • 开放地址法——二次探测

二次探测法的地址增量序列为 di = 12, -12, 22, -22,… , q2, -q2 (q <= m/2)。二次探测能有效避免“聚集”现象,但是不能够探测到哈希表上所有的存储单元,但是至少能够探测到一半。

  1. 链地址法
    链地址法也成为拉链法。其基本思路是:将所有具有相同哈希地址的而不同关键字的数据元素连接到同一个单链表中。如果选定的哈希表长度为m,则可将哈希表定义为一个有m个头指针组成的指针数组T[0..m-1],凡是哈希地址为i的数据元素,均以节点的形式插入到T[i]为头指针的单链表中。并且新的元素插入到链表的前端,这不仅因为方便,还因为经常发生这样的事实:新近插入的元素最优可能不久又被访问。

链地址法特点:

  1. 拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;

  2. 由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;

  3. 开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;

  4. 在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。

以上参考: https://blog.csdn.net/u011080472/article/details/51177412


hashtable的桶子(buckets)与节点(nodes)

SGI的hash table正是以开链(seperate chaining)实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值