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;
}
相关推荐
DeeplyMind1 分钟前
少儿科技启蒙教材:《数据结构启蒙》
数据结构·计算机科学·少儿科技读物·蓝桥杯青少组
近津薪荼3 分钟前
递归专题(2)——合并链表
c++·学习·算法·链表
划破黑暗的第一缕曙光7 分钟前
[数据结构]:6.二叉树链式结构的实现2
c语言·数据结构·二叉树
maplewen.9 分钟前
C++11 std::function
开发语言·c++
水饺编程19 分钟前
第4章,[标签 Win32] :文本尺寸的度量
c语言·c++·windows·visual studio
蒹葭玉树21 分钟前
【C++上岸】C++常见面试题目--操作系统篇(第二十九期)
java·c++·面试
浅念-25 分钟前
C语言——自定义类型:结构体、联合体、枚举
c语言·开发语言·数据结构·c++·笔记·学习·html
仰泳的熊猫34 分钟前
题目1433:蓝桥杯2013年第四届真题-危险系数
数据结构·c++·算法·蓝桥杯·深度优先·图论
cyforkk36 分钟前
14、Java 基础硬核复习:数据结构与集合源码的核心逻辑与面试考点
java·数据结构·面试
Trouvaille ~43 分钟前
【Linux】线程同步与互斥(四):线程池与任务管理
linux·运维·服务器·c++·操作系统·线程池·日志系统