13-14哈希表-跳表
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
希表。
开放定址法
对增量 di 有三种取法---②:
2) 平方(二次)探测再散列 di= 12, -12, 22, -22, …,
开放定址法
例如: 关键字集合 { 19, 01, 23, 14, 55, 68, 11, 82, 36 } 19 01 23 14 55 68 11 82 36 设定哈希函数 H(key) = key % 11 ( 表长=11 ) 采用二次探测再散列法来构造哈希表。
有唯一的编号,0,1,2,…n
存放1个或多个key-value对 2、散列(哈希Hash)函数 y=h(k) k是key-value对的关键字 y是桶编号 理想的情形下:如果k1 != k2,则y1 != y2 常用的散列函数:y = k % D, D是桶个数,一般D是素数。
散列表
3、冲突 如果k1 != k2,但y1 = y2 4、溢出
一个类如果实现了operator(),就可以像使用函数一样使用它。
int main() { const char *p = "12345"; hash d; cout << d(p)<<endl; }; 38463
class hash :public std::unary_function<const char*,size_t> { public: size_t operator()(const char * theKey) const {// Convert theKey to a nonnegative integer. unsigned long hashValue = 0; while(*theKey != 0){ hashValue = 5 * hashValue + *theKey; ++theKey; } } return size_t(hashValue);
再哈希法
Hi=RHi(key) i=1,2,3,……,k RHi均是不同的哈希函数,在同义词产生地址冲突 时计算另一个哈希函数地址,直到冲突不再发生。 缺点:增加了计算时间。
链地址法
所有关键字为同义词的记录存储在同一线性链表中。
定义指针型向量Chain ChainHash[m];
凡是哈希地址为i的记录都插入到头指针为 ChainHash[i]的链表中。
hash函数
如果C++提供的函数hash,不适用所用的数据类型,就重新定义一个hash。 例如,C++没有为 string类型提供hash函数,就像如下自己定义一个:
template <class K> class hash; template<> class hash<string> { public: size_t operator()(const string theKey) const {// Convert theKey to a nonnegative integer. unsigned long hashValue = 0; int length = (int) theKey.length(); for (int i = 0; i < length; i++) hashValue = 5 * hashValue + theKey.at(i); return size_t(hashValue);
一个桶已满
散列表
字典:{<55,v1>, <36, v2>, <19, v3>} 桶: 11个桶,编号0,1,2,…,10,假设每个桶只能存放一个key-value对
hash函数:y=k%11
55
0 1 2
36
3 4 5 6 7
19
8 9 10
哈希函数及构造方法
y = f(Key) 要求:具有均匀性
0 55 1 1 2 23 2 3 14 1 4 68 3 5 11 6 6 82 2 7 36 5 8 19 1 比较次数 9 10
01
1
课堂练习
设有一组关键字{11,54,36,89,51,47,38,59
,63,94,15},采用哈希函数:H(key) = key % 13
。采用开放地址法的线性探测再散列方法解决冲突, 试在0~12的散列地址空间中对该关键字序列构造哈
哈希函数构造方法
3. 平方取中法 以关键字的平方值的中间几位作为存储地址。求“关键 字的平方值”的目的是“扩大差别”,同时平方值的中间 各位又能受到整个关键字中各位的影响。 此方法适合于: 关键字中的每一位都有某些数字重复出现频度很高 的现象。
哈希函数构造方法
4. 折叠法 将关键字分割成若干部分,然后取它们的叠加和为哈希地 址。有两种叠加处理的方法:移位叠加和间界叠加。 此方法适合于: 关键字的数字位数特别多,而且关键字在每一位上的 数字分布大致均匀的情况。
3.如果哈希造表过程中产生冲突,应当如何处理这些冲突呢
?
处理冲突的方法
冲突:由关键字得到的哈希地址为j(0≤j≤n-1)的位置上 已存有记录 处理冲突:为产生冲突的地址寻找下一个空的哈希地址 1. 开放定址法 处理冲突方法 2. 再哈希法 3. 链地址法 4. 建立一个公共溢出区
开放定址法
为产生冲突的地址 H(key) 求得一个地址序列:
对数字的关键字可有下列构造方法: 1. 直接定址法 2. 数字分析法 3. 平方取中法 4. 折叠法 5. 除留余数法 6. 随机数法
若是非数字关键字,则需先对其进行数字化处理 。
哈希函数构造方法
1. 直接定址法 哈希函数为关键字的线性函数 H(key) = key 或者 H(key) = a ×key + b
};
hash函数
C++提供了一个仿函数hash,并特化(specialization)了一组仿函数。 实例化(instantiation)是替换模版参数产生一个函数。 而特化是针对某种类型,重新定义一个函数,和模版没有任何关系。
template<> class hash<int> { public: size_t operator()(const int theKey) const {return size_t(theKey);} }; template<> class hash<long> { public: size_t operator()(const long theKey) const {return size_t(theKey);} };
哈希函数构造方法
2. 数字分析法 假设关键字集合中的每个关键字都是由 s 位数字组成 (u1, u2, …, us),通过分析所有的关键字, 并从中提取 分布均匀的若干位或它们的组合作为地址。
此方法仅适合于: 能预先估计出全体关键字的每一位上各种数字出现 的频度。
哈希函数构造方法
有80个记录,关键字为8位十进制数,哈希地址为 2位十进制数 1 2 3 4 5 6 7 8 8 8 8 8 8 8 8 8 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 4 4 7 8 0 2 3 6 1 6 2 7 1 2 8 8 9 5 2 4 3 8 9 5 3 3 4 2 6 1 6 3 5 2 2 2 7 7 7 7 5
0 1 2 3 4 5 6 7 8 9 10
55 1
01 1
源自文库
23 2
14 1
36 2
82 1
68 4
19 1
11 3
开放定址法
对增量 di有三种取法---③:
3) 随机探测再散列 di是一组随机数列 或者 di=i× H2(key) (又称双散列函数探测)
开放定址法
注意:增量 di应具有“完备性” 即:产生的 Hi均不相同,且所产生的 m-1个 Hi值能覆盖哈希表中所有地址。 则要求: ※ 平方探测时的表长 m 必为形如 4j+3 的素数(如: 7, 11, 19, 23, … 等); ※ 随机探测时的 m 和 di没有公因子。
哈希函数构造方法
6. 随机数法
设定哈希函数为: H(key) = Random(key) 其中Random 为随机函数 此方法通常用于对长度不等的关键字构造哈希函数。
哈希函数构造方法
实际构造哈希表时 1.采用哪种构造哈希函数的方法取决于建表的关键字集合的 情况(包括关键字的范围和形态) 2.总的原则是使产生冲突的可能性尽可能的小。
字典(Dictionary)
定义:字典是key-value对的集合。
{<k1,v1>, <k2,v2>, <k3,v3>,…, <kn,vn>}
字典(Dictionary)
template<typename K, typename E> class dictionary { public: virtual ~dictionary() {}; virtual bool empty() const = 0; virtual int size() const = 0; virtual std::pair<const K, E>* find(const K&) const = 0; virtual void erase(const K&) = 0; virtual void insert(const std::pair<const K,E>&) = 0; };
其中: 1、 p>=m (表长) 且 2、 p 为素数
哈希函数构造方法
为什么要对p加限制?
给定一组关键字为: 12, 39, 18, 24, 33, 21 若取 p=9, 则它们对应的哈希函数值将为: 3, 3, 0, 6, 6, 3 可见,若p中含质因子3, 则所有含质因子3的关键字均 映射到“3的倍数”的地址上,从而增加了“冲突”的可 能。
H0, H1, H2, …, Hs 其中:H0 = H(key)
Hi = ( H(key) + di ) % m
i=1, 2, …, s m 为哈希表表长
开放定址法
1. 线性探测再散列法 增量di 的三 种取 法 2. 二次探测再散列法 3. 随机探测再散列法
开放定址法
对增量 di 的三种取法---①:
哈希函数构造方法
4. 折叠法 例 关键字为 :0442205864,哈希地址位数为4
5864 4220 0 4 移位叠加 10088 H(key)=0088
5864 0224 0 4 间界叠加 6092 H(key)=6092
哈希函数构造方法
5. 除留余数法 设定哈希函数为: H(key) = key % p
散列表
希望find操作具有O(1)的性能。 当根据下标值取数据元素时,数组具有这样的性能。 字典的find操作是根据关键字的值查找数据元素: 1、如果将数据元素存放在数组中(或顺序存储空间) 2、将关键字值转换为数组的下标 理想情况下,就能做到O(1)的性能。
散列表
1、桶(bucket): 容器
字典(Dictionary)
实现方法 1、线性表(有序或无序),采用线性表数据结构,利用比较 运算进行查找,采用顺序查找算法(O(n))或折半查找算法
(O(logn))。
2、跳表:采用链式存储结构,实现折半查找。 3、散列表(哈希表),采用顺序存储结构,利用哈希函数计
算获取数据的存储位置,直接查找。
链地址法
例如: 关键字集合 { 19, 01, 23, 14, 55, 68, 11, 82, 36 } 19 01 23 14
55 68 11 82 36
0
14 ∧ 01 23 ∧ 36 ∧
1
2
哈希函数为 H(key)=key MOD 7
采用链地址法来构造哈希表。 ASL=(6× 1+2× 2+3× 1)/9=13/9
3 ∧
4 5 6
11 ∧ 19 55 ∧ 68 82 ∧
公共溢出区
哈希函数的值域[0,m-1] 向量HashTable[0..m-1]为基本表,每个分量存放一个记录
向量OverTable[0..v]为溢出表
对于关键字和基本表HashTable中关键字为同义词的记录 ,只要发生冲突,都填入溢出表。
C++仿函数(functional object)
开放定址法
H2(key) 是另设定的一个哈希函数,它的函数值应和 m 互为素数。 若 m 为素数,则 H2(key) 可以是 1 至 m-1 之间的任意数;
若 m 为 2 的幂次,则 H2(key) 应是 1 至 m-1 之间的任意 奇数。
开放定址法
例如,当 m=11时, 可设 H2(key)=(3 key) % 10+1 55 1 01 1 68 1 14 1 11 2 1 82 1 19 2 36 2 23
1) 线性探测再散列 di= c× i 最简单的情况 c=1 即 di= 1,2,3,……m-1
开放定址法
例如: 关键字集合 { 19, 01, 23, 14, 55, 68, 11, 82, 36 } 19 01 23 14 55 68 11 82 36 设定哈希函数 H(key) = key % 11 ( 表长=11 ) 采用线性探测再散列法来构造哈希表。