哈希查找Hash locating

目录

1.1哈希表的含义

Hash表的含义

1.2哈希函数(表)的构造方法

直接地址法

除留余数法

1.3处理冲突的方法

开放地址法

链地址法


1.1哈希表的含义

Hash表的含义

1)Hash 表,又称散列表。在前面讨论的顺序、折半、分块查找和树表的查找中,其 ASL 的量级在
O(n) O(log2n) 之间。
2)不论 ASL 在哪个量级,都与记录长度 n 有关。随着 n 的扩大,算法的效率会越来越低。 ASL n
关是因为记录在存储器中的存放是随机的,或者说记录的key 与记录的存放地址无关,因而查找只
能建立在 key 的“比较”基础上。
3)理想的查找方法是:对给定的 key ,不经任何比较便能获取所需的记录,其查找的时间复杂度为常
数级 O(C) 。这就要求在建立记录表的时候,确定记录的key 与其存储地址之间的关系 f ,即使 key
记录的存放地址 H 相对应:

或者说,记录按key存放。
        之后,当要查找key=k 的记录时,通过关系 f 就可得到相应记录的地址而获取记录,从而免去
key 的比较过程。这个关系f 就是所谓的 Hash 函数(或称散列函数、杂凑函数),记为 H(key) 。它
实际上是一个地址映象函数,其自变量为记录的key ,函数值为记录的存储地址(或称 Hash
址)。
以上通俗来说就是将一串数据按照一个函数(这里可以简单理解为加减乘除)规则计算地址,并按
照这个地址进行存储。
查找数据时,就把要查找的数据进行之前编码的函数计算从而获得数据的地址,然后直接
找到它。
举个例子:
                张三                李四                王五                林朝夕
以上是我要存储的数据
                假如计算地址的函数是提取每个数据的第一个字符
那我的数据地址就为
                王                林                李                张                 (顺序无关)
数据存储形式就为

             
所以,当我要查找   林朝夕  这个数据就可以通过函数计算得到       这个地址,从而直接找到
朝夕这个数据。
那么这时候就有聪明的小伙伴会问,如果有同姓的名字或者完全相同的名字怎么办?
接下来我们就来解决这个问题
                
另外,不同的 key 可能得到同一个 Hash 地址,即当 key l ≠key 2 时,可能有 H(key 1 )=H(key 2 ) ,此时称
key 1 key 2 同义词
这种现象称为“ 冲突 ”或“碰撞”,因为一个数据单位只可存放一条记录。
一般,选取 Hash 函数只能做到使冲突尽可能少,却不能完全避免。这就要求在出现冲突之后,寻
求适当的方法来解决冲突记录的存放问题。
综上根据选取的 Hash 函数 H(key) 和处理冲突的方法,将一组记录 (R 1 R 2 ……R n ) 映象到记录的存储
空间,所得到的 记录表称为Hash表,如图:

1.2哈希函数(表)的构造方法

关于Hash表的讨论关键是两个问题,一是选取Hash函数的方法;二是确定解决冲突的方法。

选取(或构造) Hash 函数的方法很多,原则是尽可能将记录均匀分布,以减少冲突现象的发生。
以下介绍两种常用的构造方法。
直接地址法
平方取中法
叠加法
除留余数法
随机函数法

直接地址法

此方法是取key的某个线性函数为Hash函数,即令:
H(key)=a · key+b
其中a、b为常数,此时称H(key)为直接Hash函数或自身函数。
这类哈希函数是一对一的映射,一般不会产生冲突。但是,它要求哈希地址空间的大小与关键字集
合的大小相同。例如,有一个从1~100岁的人口数字统计表,其中,年龄作为关键字(就是要存储
的数据),哈希函数取关键字自身(a = 1,b = 0),其哈希表如下表所示
地址0102...252627...100
年龄12...252627......
人数30002000...1050............
...

除留余数法

又称质数除余法,设Hash表空间长度为m,选取一个不大于m的最大质数p,令:
H(key)=key%p
例如:m=8 16 32 64 128 256 512 1024 ……
p=7 13 31 61 127 251 503 1019 ……
举个例子:
设记录的key集合k={28,35,63,77,105 …… }, 若选取p=21=3*7(包括质数因子7) ,有:
key:28 35 63 77 105 ……
H(key)=key%21: 7 14 0 14 0 ……
使得包含质数因子7的key都可能被映象到相同的单元,冲突现象严重。
若取p=l9(质数),同样对上面给定的key 集合k ,有:
key:28 35 63 77 105
H(key)=key%19: 9 16 6 1 10
H(key)的随机度就好多了。

1.3处理冲突的方法

以上是构造哈希函数避免冲突,但我们往往发现依然存在少量冲突,所以我们要寻求解决冲突的方法。

开放地址法

当发生冲突时,在H(key)的前后找一个空闲单元来存放冲突的记录,即在H(key)的基础上获取下一
地址: H i =(H(key)+d i )%m
其中m为表长,%运算是保证H i 落在[0,m-l]区间;d i 为地址增量。d i 的取法有多种:
(1)di=1,2,3,……(m-1)——称为线性探查法;
(2)di=12,-12,22,-22……——称为二次探查法。
式(1)、(2)表示:第1次发生冲突时,地址增量d1取1或12;再冲突时,d2取2或-l2……,依此类推。

设记录的key集合 k={23,34,14,38,46,16,68,15,07,31,26} ,记录数 n =11。令装填因
α =0.75, 取表长 m = n/α   =15。用“保留余数法”选取Hash函数( p =13):
H(key)=key%13
采用“线性探查法”解决冲突。依据以上条件,依次取k中各值构造的Hash表HT,如下图所示(表
HT初始为空)
229
k={23 34 14 38 46 16 68 15 07 31 26}
H(key)=key%13; H i =(H(key)+d i )%15; d i =1 2 3 …… (m- 1

H(68)=68%13=3( 冲突 ), H 1 =(3+1)%15=4( ), 68 存入 4 单元。
15
68
H(07)=7%13=7( 冲突 ), H 1 =(7+1)%15=8( 冲突 ), H 2 =(7+2)%15=9( ), 07 存入 9 单元。
07
31
26
若采用二次探测法: d i =1 2 -1 2 2 2 -2 2 …… ,表为:

 

其中, H(07)=7%13=7( 冲突 ) , 取 H 1 =(7+ 1 2 )%15=8( 冲突 ) ,取 H 2 =(7 -1 2 )%15=6( ) ,故 07
6 单元。

#define m 64 //设定表长m//

typedef struct

{ keytype key; //记录关键字
        ……
}Hretype;

Hretype HT[m]; //Hash表存储空间//

int Lhashsearch(Hretype HT[m],keytype k) //线性探查法解决冲突时的查找//

{

int j,d,i=0;

j=d=H(k); //求Hash地址并赋给j和d//

while((i<m)&&(HT[j].key!=NULL)&&(HT[j].key!=k))

{ i++; j=(d+i)%m; } //冲突时形成下一地址//

if(i==m) return(-1); //表溢出时返回-1//

else return(j);

} //HT[j].key==k,查找成功;HT[j].key==NULL,查找失败//

void LHinsert(Hretype HT[m], Hretype R) //记录R插入Hash表的算法//

{

int j=LHashsearch(HT,R.key); //查找R,确定其位置//

if((j= = -1)||(HT[j].key==R.key)) ERROR(); //表溢出或记录已存//

else HT[j]=R;

//插入HT[j]单元//
}

链地址法

发生冲突时,将各冲突记录链在一起,即同义词的记录存于同一链表。
设H(key)取值范围(值域)为[0,m-l],建立头指针向量HP[m],HP[i](0≤i≤m-l)初值为空。凡
H(key)=i的记录都
链入头指针为HP[i]的链表。
例10 设H(key)=key%13,其值域为[0,12], 建立指针向量 HP[l2]。 对例9中:
k={ 23,34,14,38,46, 16,68,15,07,31,26 }
依次取其中各值,用链地址法
解决冲突时的Hash表如

 

230
链地址法解决冲突的优点:无聚积现象;删除表中记录容易实现。而开放地址法的Hash表作删除
时,不能将记录所在单元置空,只能作删除标记。
typedef struct node //记录对应结点//

{

keytype key;

……

struct node *next;

}Renode;

Renode *LinkHsearch(Renode *HT[m],keytype k) //链地址法解决冲突时的查找//

{

Renode *p;

int d=H(k); //求Hash地址d//

p=HT[d]; //取链表头结点指针//

while(p&&(p->key!=k))

p=p->next; //冲突时取下一同义词结点//

return(p);

}

//查找成功时p->key==k,否则p=NULL//

当然还有    再哈希法   和     建立公共溢出区法

221
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值