HASH表

HASH函数构造

构造函数的常用方法(下面为了叙述简洁,设 h(k) 表示关键字为 k 的元素所对应的函数值):

为简单起见,假定关键码是定义在自然数集合上,常见的哈希函数构造方法有:

1、直接定址法

以关键字Key本身或关键字加上某个数值常量C作为散列地址的方法。散列函数为:h(Key)= Key+C,若C为0,则散列地址就是关键字本身。

2、除余法

选择一个适当的正整数m,用m去除关键码,取其余数作为地址,即:h(Key)= Key % m,这个方法应用的最多,其关键是m的选取,一般选m为小于某个区域长度n的最大素数(如例1中取m=13),

为什么呢?就是为了尽力避免冲突。假设取m=1000 ,则哈希函数分类的标准实际上就变成了按照关键字末三位数分类,这样最多1000类,冲突会很多。

一般地说,如果 m 的约数越多,那么冲突的几率就越大。而素数的约数是最少的,因此我们选用大素数。记住"素数是我们的得力助手"。

3、数字分析法

常有这样的情况:关键码的位数比存储区域的地址的位数多,在这种情况下可以对关键码的各位进行分析,丢掉分布不均匀的位留下分布均匀的位作为地址。

本方法适用于所有关键字已知,并对关键字中每一位的取值分布情况作出了分析。

【例】 对下列关键码集合(表中左边一列)进行关键码到地址的转换,要求用三位地址。

分析:

键码是9位的,地址是3位的,需要经过数字分析丢掉6位。丢掉哪6位呢?显然前3位是没有任何区分度,第5位1太多、第6位基本都是8和9、第7位都是3、4、5,这几位的区分度都不好,而相对来说,第4、8、9位分布比较均匀,所以留下这3位作为地址(表中右边一列)。

4、平方取中法

将关键码的值平方,然后取中间的几位作为散列地址。具体取多少位视实际要求而定,取哪几位常常结合数字分析法。

【例】

将一组关键字(0100,0110,1010,1001,0111)平方后得(0010000,0012100,1020100,1002001,0012321),若取表长为1000,则可取中间的三位数作为散列地址集:(100,121,201,020,123)。

5、折叠法

如果关键码的位数比地址码的位数多,而且各位分布较均匀,不适于用数字分析法丢掉某些数位,那么可以考虑用折叠法。折叠法是将关键码从某些地方断开,分关键码为几个部分,其中有一部分的长度等于地址码的长度,然后将其余部分加到它的上面,如果最高位有进位,则把进位丢掉。

一般是先将关键字分割成位数相同的几段(最后一段的位数可少一些),段的位数取决于散列地址的位数,由实际需要而定,然后将它们的对应位叠加和(舍去最高位进位)作为散列地址。

6、基数转换法

将关键码值看成在另一个基数制上的表示,然后把它转换成原来基数制的数,再用数字分析法取其中的几位作为地址。一般取大于原来基数的数作转换的基数,并且两个基数要是互质的。

冲突处理

线性重新散列技术易于实现且可以较好的达到目的。令数组元素个数为 S ,则当 h(k) 已经存储了元素的时候,依次探查 (h(k)+i) % S , i=1,2,3...... ,直到找到空的存储单元为止(或者从头到尾扫描一圈仍未发现空单元,这就是哈希表已经满了,发生了错误。当然这是可以通过扩大数组范围避免的)。

涉及运算

哈希表支持的运算主要有:初始化(makenull)、哈希函数值的运算(h(x))、插入元素(insert)、查找元素(member)。

设插入的元素的关键字为 x ,a 为存储的数组。

初始化
复制代码
int p=9997;                             //hash表的大小
void makenull()
{
  for(int i=0;i<=p-1;i++)     //存储数组初始为0;
    a[i]=0;
  return;
}

哈希函数值的运算根据函数的不同而变化,例如除余法的一个例子:

复制代码
int h(int x){
	return x%p;
}

我们注意到,插入和查找首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位置,因此加入一个定位的函数 locate

复制代码
int locate(int x){
    int orig=h(x);
    int i=0;
    while(i<s&&a[(orig+i)%s]!=x&&a[(orig+i)%s]!=0)
        i++;
    return (orig+i)%s;
}

当这个循环停下来时,要么找到一个空的存储单元,要找到这个元素的存储位置,要么hash已满

插入元素
复制代码
void insert(int x){
	int posi=locate(x);  //定位函数的返回值
 	if(a[posi]==0)  a[posi]=x;
   	else error;  //error 即为发生了错误,当然这是可以避免的,如开大数组
   }  
查找元素是否已经在表中
复制代码
bool member(int x){
	int posi=locate(x);
   	if (a[posi]==x)  return true
   	else return false;
}
相关推荐
superman超哥2 小时前
仓颉语言中锁的实现机制深度剖析与并发实践
c语言·开发语言·c++·python·仓颉
郝学胜-神的一滴3 小时前
OpenGL的glDrawElements函数详解
开发语言·c++·程序人生·游戏·图形渲染
WBluuue3 小时前
AtCoder Beginner Contest 436(ABCDEF)
c++·算法
辣机小司3 小时前
【软件设计师】自编思维导图和学习资料分享(中级已过)
java·c++·软考·软件设计师
json{shen:"jing"}4 小时前
1-C语言的数据类型
c语言·c++·算法
im_AMBER4 小时前
数据结构 13 图 | 哈希表 | 树
数据结构·笔记·学习·算法·散列表
LYFlied4 小时前
【算法解题模板】动态规划:从暴力递归到优雅状态转移的进阶之路
数据结构·算法·leetcode·面试·动态规划
名誉寒冰4 小时前
GDB 调试与 Core Dump(段错误)排查指南(Linux/C/C++)
linux·c语言·c++
合方圆~小文5 小时前
4G定焦球机摄像头综合介绍产品指南
数据结构·数据库·人工智能