散列结构

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

算法与数据结构
43
template <class T> int openHashVector<T>::includes(T & value) { unsigned start = (hashfun)(value) % size; unsigned index = start; T curValue = data[index]; while (curValue != value) // 未找到对应值就继续循环 { if (curValue == spareValue) return 0; index = (index + 1) % size; if (index == start) return 0; curValue = data[index]; } return 1; }
算法与数据结构
31
在实际工作中一般认为选取B为适当的素数为好。 例如: B = 7,13,31,61,127,251,503,1019
算法与数据结构
32
除余法的地址计算公式简单。然而实践证明,恰恰 是这种简单的方法在许多情况下效果比较好。需要 注意的是B的选择,也就是散列表中桶的个数的确 定,因为用除余法所计算的结果一定在0 ~ B-1的 范围之中。
算法与数据结构
22
2)折叠:先把关键码分割成小块,各部分的值必要
时可以先散列处理,然后再混合起来便形成整个关 键码的散列值。 混合又有多种不同的方法,如加法、乘法或逻辑 运算。
算法与数据结构
23
下面的小程序段实现了一种把字符串str变成整数 值的过程,所用的方法便是将所有字母值加起来。
unsigned int hashval = 0; int i = str.length( ); while (i > 0) hashval += str[i];
算法与数据结构
29
散列函数的第二步工作是将已得到的整数值转化 成散列表的合法下标值,这时采用最多的是除余 法。 所谓除余法,就是选择一个恰当的正整数B,用B 去除前一步骤得到的整数,取其余数作为散列表 的下标值。
算法与数据结构
30
这个方法的关键是选取恰当的B。如果B选为一个 偶数,则它将总是把奇整数值转换到奇数的下标, 把偶整数值转换到偶数的下标,这当然不太好。 选B为10的幂次就更不好,因为那样就等于选择 整数的最后几位数字作为地址。例如,选B=100, 则实际上就是取整数的最后两位作为地址。
算法与数据结构
5
下面将首先介绍散列结构的基 本思想,然后重点讨论散列结 构实现的两个基本要素:散列 函数和“碰撞”的各种解决技 术。本章的后面部分把散列结 构作为集合和字典的重要实现 技术,讨论这些方面的有关问 题。
算法与数据结构 6
10.1 基本概念
算法与数据结构
7
存储方式和检索方法: 散列函数 关键码——>——> 地址(固定,连续空间)
算法与数据结构 20
对一般类型的关键码,运用散列函数的过程通常 分成两步: 第一步是将关键码转化成适当的整数值;
第二步是将得到的整数转化为散列向量的合法下 标。
算法与数据结构
21
实现第一步有很多种方法,下面列举常见的几种。
1)映射:将关键码以某种数值方式映射成整数值。
对于比较复杂的映射,一般可用一个映射数组来 定义。数组中的元素都在程序编制前预先给出。 程序执行时,求散列值信息就是在映射数组中找 到该元素,对于一组确定的关键码来说,这是产 生一个理想的散列函数的基本方法。
算法与数据结构 3
散列结构与枚举向量有类似之 处:这里采用的基本存储结构 也是一个向量,要做的也是从 关键码出发,直接确定数据元 素的存储位置。但是散列结构 更具一般性,原则上说它允许 任意种类的关键码。
算法与数据结构 4
设计良好的散列结构可以具有 非常高的操作效率,因此这种 结构在计算机领域中应用广泛, 是一种非常有效的存储和查询 结构。
template <class T> openHashVector<T>::openHashVector (int max, int(f)(const T &),T & value) : hashfun(f), data(max), spareValue(value) { // 设置向量大小 size = data.length( ); for (unsigned i = 0; i<size; i++) data[i] = spareValue; }
算法与数据结构 42
template <class T> int openHashVector<T>::add (const T & v) { // 把一个确定数据记录存放到开地址散列向量 unsigned start = (hashfun)(v) % size, index = start; // 检查位置是否已被占用 while (data[index] != spareValue) { index = (index + 1) % size; // 考察下一个位置 if (index = = start) return 0; } data[index] = v; // 发现空位,插入新元素 return 1; }
算法与数据结构
12
数,一个是向量的索引(关键 码)类型H,另一个是向量元 素类型T。
算法与数据结构
13
initialValue); simpleHashVector(const simpleHashVector<H, T> & v); //下标操作 T & operator [ ] (const H & index); private: // 记录散列函数的函数指针 const int (hashfun)(const H &);[qzy1] };
key 000319426 000718309 000629443 000758615 000910497 000310329 h(key) 326 709 643 715 997 329
算法与数据结构
28
这个方法的缺点是散列函数依赖于关键码集合, 具体到这6个关键码值,经过分析留下第4、8、9 位,若换一个关键码集合,就要重新分析,也许 留下第3、6、7位。通过对比较大的实例集合进行 分析,就可能得到比较合理的结果。
算法与数据结构
16
种广义“下标”)转换成一个 适当的整数值,然后再把该整 数转换成对于向量合法的下标 值。后一转换一般直接采用按 向量大小取余数的方式。
算法与数据结构
17
return vector<T>::operator[ ]((hashf un)(index) % size); }
简单散列向量类的其他成员函 数都非常简单。读者不难自己 实现它们。 10.2 散 列 函 数
算法与数据结构 18
理想的散列函数是个一对一映 射,它能把有关的一组关键码 映射到连续的一组整数值,每 个关键码对应一个整数值,反 之依然。
算法与数据结构
19
假设用h(x)代表散列函数,一般来说,h应该满足 三个条件: h的定义域是全体可能出现的关键码的集合,h 的值域是散列表空间位置(向量下标)的集合; 希望函数值分布对于散列空间位置而言要尽量均 匀; h函数应该是足够简单的。
算法与数据结构
26
4)数字分析法:常常有这样的情况:关键码的位
数比存储区域的地址码的位数多,同时其中各位 的出现不是随机的,在这种情况下可以通过事先 对关键码的各位情况进行分析,散列时丢掉分布 不均匀的位,留下分布均匀的位。
算法与数据结构
27
例如,需要对下列关键码集合进行转换,实际关 键码有9位数字,经过数字分析法可以丢掉6位。
“散列”(hash)一词的意思是 进行某种混合性的变换,从关键 码本身看,这种变换可能并没有 很清楚的具体意义。散列这个词 在有些中文教科书或文献中被译 为“杂凑”,散列结构也被称为 “散列表”、“杂凑表”,或者 按照音译称为“哈希表”等。
算法与数据结构 11
列向量类)。这里把它定义为 向量类Vector的一个子类。简 单散列向量比普通向量多一个 数据成分,那就是一个指向散 列函数的指针hashfun。对具体 的简单散列向量而言,这个数 据成分是在构造时建立,是一 种不能改变的性质。
算法与数据结构
24
3)移位:移位可以避免在折叠处理中由于运算的可
交换带来的碰撞。 如上段程序便对apt、tap、pat会产生相同的散 列值,而使用移位便可能减少这些碰撞。下面是加 入移位处理后的程序段:
算法与数据结构
25
unsigned int hashval = 0; int i = str.length( ); while (i > 0) hashval = (hashval << 1) + str[1];
算法与数据结构
33
同义词 负载因子
碰撞(开地址,桶)
算法与数据结构
34
两个关键码散列成相同的值的现象被称为“碰 撞”。碰撞的产生使散列的使用产生了问题。
算法与数据结构
35
下面我们先引入“同义词”的概念。设有关键码 k1和k2,经过散列函数f的作用,把它们映射成相 同的值,即f(k1)= f(k2),称k1和k2在函数f的解释 下含义相同,简称为“同义词”。显然同义词越 多,碰撞的可能性就越大。
算法与数据结构
36
另外一个重要概念是“负载因子”。简单地讲,一 种结构的负载因子可以定义为这个结构中实际元 素的个数与能够容纳的元素个数之比。即: = 结构中实际元素的个数 / 结构中能够容纳的元 素个数
算法与数据结构
37
10.3 开地址散列向量
算法与数据结构
38
为了解决碰撞,人们提出了许 多方法,本节先介绍其中的一 种,称为“开地址法”。用这 种方法建立的散列结构称为 “开地址散列向量”。
第十章 散 列 结 构
枚举向量,这种向量中使用的 下标不是整数,而是某枚举类 型的实例。 对枚举向量,查询的出发点是 一个枚举值,要做就是由这个 枚举值出发,确定有关数据元 素的存储位置。
算法与数据结构 2
作为查询出发点的值可能有各 种不同情况,需要进一步研究 有关的技术和方法。 在集合与字典的一章里,已经 讨论了一些结构和技术,那里 使用的基本技术是关键码比较。
算法与数据结构
40
template <class T> class openHashVector { public: //构造函数 openHashVector(int max,int(f)(const T &),T & value); openHashVector(const openHashVector<T> &); //操作 int add(const T &); int includes(T &); void remove(const T &); private: //数据域:散列函数,基向量和向量长度及初值 const int (hashfun)(const T &); vector<T> data; int size; const T spareValue; }; 41 算法与数据结构 类10.2 openHashVector类规范说明
算法与数据结构
39
类10.2给出openHashVector的规范说明。为 了叙述简单,我们假设向量中每个元素都只 包含关键码自身,元素类型就是关键码类型。 这时模板类的参数只需要一个元素类型T (也是关键码类型);进一步还假设对T类 型有一个特定值,这个值不表示任何真正有 意义的关键码。因此,如果向量中某元素取 这个值,就表明该元素处于空闲状态。
算法与数据结构
8
“散列”一词的意思是进行某种随机的混合,有 的中文教科书或文献中把它称为“杂凑”,对应 的英文词是一样的——hash。
算法与数据结构
9
散列函数是ห้องสมุดไป่ตู้种数据转换函数,它的参数应该是 某种查询的依据,一般称为“关键词”或“关键 码”,而函数返回的是一个无符号整数值,作为 确定下标的依据。 散列的基本想法就是构造一个对应关系,把每个 关键码对应到一个整数(存储位置)。这样,如 果需要存储与某个关键码相关的数据,就将它存 到与关键码对应的存储位置里去;如果需要查询 与某个关键码有关的信息,就到与这个关键码对 应的位置中去找。 算法与数据结构 10
算法与数据结构
14
简单散列向量类的定义与枚举 向量有相似的地方,例如对它 们都可以用字符序列形式的向 量下标直接存取相关元素。
算法与数据结构
15
的字符序列必须是某个枚举类 型的值,下标操作的实现是由 系统自动将枚举值转换成内部 整数值。而对于简单散列向量, 则可以使用真正的字符串,这 种字符串可以送去打印等。
相关文档
最新文档