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;
}
相关推荐
会周易的程序员2 分钟前
企业私有 AI 算力服务器架构设计:异构四节点 + QUIC 微服务
运维·服务器·c++·人工智能·微服务·架构
m0_734571765 分钟前
深入理解C++ 类型转换<三>const_cast
开发语言·c++
库玛西12 分钟前
Linux网络编程:HTTP/HTTPS协议核心技术全解析
linux·运维·服务器·网络·c++·http·https
每周报刊1 小时前
竞速座舱级视界:爱攻 AGP497UCZD 超宽 QDOLED 显示器解析
数据结构·游戏·计算机外设
布莱克6052 小时前
链表详解:定义、作用、应用场景及与数组的区别(C/C++ 实战)
c语言·开发语言·c++·链表
执子星海3 小时前
Registry Win32 API详解
c++·windows·microsoft·visualstudio·微软
江屿风3 小时前
C++OJ题经验总结(竞赛)5
开发语言·c++
小星星闪亮登场3 小时前
图论--最小生成树(内含二分图)
数据结构·算法·图论·迭代加深·图搜索算法
zh路西法4 小时前
【玩转VLA具身智能机械臂】(三):视觉避障——从 RGB-D 点云到 MoveIt octomap 的完整落地
c++·ros2·gazebo·moveit2·ompl·octomap
2601_956121974 小时前
线性DP(入门)
c++·算法·动态规划