哈希表

哈希表的定义

哈希表是一种查找方式。在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。因而在查找时,只要根据这个对应关系f找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上,由此,不需要进行比较便可直接取得所查记录。再次,我们称这个对应关系f为哈希函数,按这个思想建立的标为哈希表。

(1)哈希函数是一个映像,因此哈希函数的设定很灵活,只要使得任何关键字由此所得的哈希函数值都落在表长允许范围之内即可;
(2)对不同的关键字可能得到同一哈希地址,即key1 ≠ key2,而f(key1)=f(key2),这种现象称冲突。具有相同函数值的关键字对该哈希函数来说称做同义词
然而,在一般情况下,冲突只能尽可能地少,而不能完全避免。因为,哈希函数是从关键字集合到地址集合的映像。通常,关键字集合比较大,它的元素包括所有可能的关键字,而地址集合的元素仅为哈希表中的地址值。
因此,在建设哈希表时不仅要设定一个“好”的哈希函数,而且要设定一种处理冲突的方法。

有上述定义哈希表:根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映像到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为哈希表,这一映像过程称为哈希造表或散列,所得存储位置称哈希地址或散列地址。

哈希函数的构造方法

若对于关键字集合中的任一个关键字,经哈希函数映像到地址集合中任何一个地址的概率是相等的,则称此类哈希函数为均匀的哈希函数。换句话说,就是使关键字经过哈希函数得到一个“随机的地址”,以便使一组关键字的哈希地址均匀分布在整个地址区间中,从而减少冲突。
常用的构造哈希函数的方法有:
1、直接定址法
取关键字或关键字的某个线型函数值为哈希地址。即:
H(key)= key或H(key)= a × key + b
其中a和b为常数(这种哈希函数叫做自身函数)

2、数字分析法
假设关键字是以r为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。

3、平方取中法
取关键字平方后的中间几位为哈希地址。这是一种较常用的构造哈希函数的方法。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取得位数由表长决定。

4、折叠法
将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。关键字位数很多。而且关键字中每一位上数字分布大致均匀时,可以采用折叠法得到哈希表地址。

5、除留余数法
取关键字被某个不大于哈希表表长m的数p除后所得余数为哈希地址。即
H(key)= key MOD p, p <= m
这是一种最简单,也最常用的构造哈希函数的方法。它不仅可以取关键字直接取模,也可在折叠、平方取中等运算之后取模。

6、随机数法
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)= random(key),其中random为随机函数。通常,当关键字长度不等时采用此法构造哈希函数较恰当。

实际工作中需视不同的情况采用不同的哈希函数。通常,考虑的因素有:
(1)计算哈希函数所需时间(包括硬件指令的因素);
(2)关键字的长度
(3)哈希表的大小
(4)关键字的分布情况
(5)记录的查找频率

处理冲突的方法

1、开放定址法

开放定址法就是从发生冲突的那个单元开始,按照一定的次序,从哈希表中查找出一个空闲的存储单元,把发生冲突的待插入元素存入到该空闲单元的一类处理冲突的方法。在开放定址法中,哈希表中的空闲单元(假定下标为i)不仅向哈希地址为i的同义词的元素开放,允许它们使用,也向发生冲突的其他元素开放,因它们 的哈希地址不为i,所以称之为非同义词元素。

总之,在开放定址法中,空闲单元不仅向同义词开放,也向发生冲突的非同义词开放,因此称之为开放定址法。

假设哈希表的地址集为0~n-1,冲突是指关键字得到的哈希地址为j(0<=j<=n-1)的位置上已经有关键字存在,则冲突处理就是为该关键字找到一个“空”的哈希地址。在处理冲突的过程中可能得到一个一个地址序列,Hi (i=1,2,3,4…k,),Hi介于0到n-1之间,即在处理哈希地址的冲突时,若得到另一个哈希地址H1仍然发生冲突,则再求下一个地址H2,若H2仍然冲突,则再求H3,以此类推,直至Hk不发生冲突为止,则Hk为记录在表中的地址。

2、再哈希法
Hi = RHi(key) i = 1,2,……,k
RHi均是不同的哈希函数,即在同义词产生地址冲突时计算另一个哈希函数地址,直到冲突不再发生。这种方法不易“聚集”,但增加了计算的时间。

3、链地址法
将所有关键字为同义词的记录存储在同一线性链表中。假设某哈希函数产生的哈希地址在区间[ 0,m-1]上,则设立一个指针型向量
Chain ChainHash[m];
其每个分量的初始状态都是空指针。凡哈希地址为i的记录都插入到头指针为ChainHash[ i ]的链表中。在链表中的插入位置可以在表头或表尾;也可以在中间,以保持同义词在同一线性链表中按关键字有序。

4、建立一个公共溢出区
假设哈希函数的值域为[0, m - 1],则设向量HashTable[0..m-1]为基本表,每个分量存放一个记录,另设立向量OverTable[0..v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。

哈希表的查找及其分析

给定K值,根据造表时设定的哈希函数求得哈希地址,若表中此位置上没有记录,则查找不成功;否则比较关键字,若和给定值相等,则查找成功;否则根据造表时设定的处理冲突的方法找“下一地址”,直至哈希表中某个位置为“空”或者表中所填记录的关键字等于给定值时为止。

以开放地址处理冲突的哈希表的查找过程

Status SearchHash(HashTable H, KeyType K, int &p, int &c)
{
    p = Hash(K);
    while(H.elem[p].key != NULLKEY && !EQ(K, H.elem[p].key) )
        collision(p, ++c);
    if EQ(K,H.elem[p].key)
        return SCCESS;
    else
        return UNSUCCSS;

调用查找算法实现开放定址哈希表的插入操作。

Status InsertHash(HashTable &H, Elemtype e)
{
    c = 0;
    if( SearchHash (H, e.key, p, c) )
        return DUPLICATE;
    else if ( c < hashsize[ H.sizeindex] / 2 ){
        H.elem[ p ] = e;
        ++ H.count;
        return OK;
    } else {
        RecreateHashTable(H);
        return UNSUCCESS;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值