0.散列表的定义
<0>定义:根绝键(Key)而直接访问内存位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需要查询的数据映射到表中的一个位置来访问记录,加快了查找速度。这个映射函数称作散列函数,存放记录的数组称作散列表。
<1>一些基本的概念
(1):若关键字为k,则其值存放在f(k)的存储位置上。由此,不需要比较便可以直接取得所查的记录。成哥这对应关系f为散列函数,建立的表为散列表。
(2):对不同关键子可能得到同意散列地址,即k1 != k2, 但是f(k1) == f(k2),这种现象称为冲突。
(3):若对于关键字集合中的任意一个关键字,经散列函数映射到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数,这使得关键字经过散列函数得到的是一个“随机的地址”,从而减少了冲突。
1.散列表的抽象数据类型
类型名称:符号表(SymbolTable)
数据对象集:符号表是"名字(Name)-属性(Attribute)"对的集合
操作集:
1.SymbolTable InitializeTable(int TableSize);创建一个长度为TableSize的符号表
2.bool IsIn(SymbolTable Table, NameType Name);查找特定名字Name是否在符号表Table中
3.AttributeType Find(SymbolTable Table, NameType Name);获取Table中指定名字Name对应的属性
4.SymbolTable Modefy(SymbolTable Table, NameType Name, AttributeType Attr);将Table中指定名字Name的属性修改为Attr
5.SymbolTable Insert(SymbolTable Table, NameType Name, AttributeType Attr);向Table中插入一个新名字Name及其属性Attr
6.SymbolTable Delete(SymbolTable Table, NameType Name);从Table中删除一个名字Name及其属性
2.散列函数的构造方法
<0>:一个“好的”散列函数一般会考虑一下两个因素:
(1):计算简单,以便提高运转速度
(2):关键词对应的地址空间分布均匀,以尽量减少冲突
<2>:对于数字关键词的散列函数构造
(1):直接定址法:取关键词的某个线性函数值为散列地址,即h(key) = a*k + b;
(2):除留余数法:散列函数为:h(key) = key % p; 一般的p为TableSize或者质数。
(3):数字分析法:分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址;比如所有号码的后四位作为地址。
(4):折叠法:把关机那次分割成位数相同的几个部分,然后叠加:如56793542 ---->542 + 793 + 056 = ---->1391---->391
(5):平方取中法:如56793542---->56793542 * 56793542 = 322550(641)2905764
<3>:字符关键词的散列函数构造
(1):一个简单的散列函数----ASCII码加和法:对字符型关键词key定义散列函数如下:
(2):简单的改进-----前三个字符移位法:h(key) = (key[0] * 27^2 + key[1] * 27 + key[2] * 1) mod TableSize;
(3):好的散列函数------移位法:涉及关键词的所有n个字符,并且分布的很好:
<4>:装填因子:设散列表空间大小位m,填入表中的元素个数是n,则a = n / m称为列表的装填因子。
3.冲突处理方法
<1>:常用处理冲突的思路:
(1):换一个位置:开放地址法
(2):同一位置的冲突对象组织在一起:链地址法
<2>:开放地址法:一旦产生冲突,就按某种规则去寻找另一空地址
若发生了第i次冲突,试探的下一个地址将增加di,基本公式是:
hi(key) = (h(key) + di) mod TableSize;
di决定了不同的解决冲突的方案:线性探测、平方探测、双散列
(1):线性探测法:以增量序列1,2,...,(TableSize - 1)循环探测下一个存储地址
(2):平方探测法----二次探测:以增量序列1^2, - 1^2, 2^2, - 2^2, ... . q^2, - q^2,且
注意:平方探测法存在当HashTable还有空间时,但是并不能找到的特点。
有定理显示:如果散列表的长度TableSize是某个4k + 3(k 是正整数)形式的素数时,平方探测法就能探测到整个散列表空间。、
Ps:在开放地址散列表中,删除操作要很小心。通常只能“懒惰删除”,即需要增加一个“删除标记(Deleted)”,而并不是真正的删除它,一遍超找不到时会"断链"。其空间可以在下次插入时重用。
(3):双散列探测法(Double Hashing):di为i*h2(key),h2(key)是另外一个散列函数,探测序列:h2(key), 2h2(key), 3h2(key), ... .
i:对任意的key,h2(key) != 0
ii:探测序列还应该保证所有的散列存储单元都应该能够被探测到,选择一下形式具有良好的效果:h2(key) = p - (key mod p);其中p,TableSize都是素数
(4):再散列
i:当散列表的元素太多时(即装填因子a太大时),查找效率会下降。实用的最大装填因子:0.5 <= a <= 0.85
ii:当装填因子过大时,解决的方法是加倍扩大散列表,这这过程叫做“再散列”
iii:分离链式法:将相应位置上冲突的所有关键词存储在同一个单链表中。
4.散列表的性能分析
<1>:平均查找长度(ASL)用来衡量散列表的查找效率:成功、不成功
<2>:关键词的比较次数,取决于产生冲突的多少。影响产生冲突多少有以下三个因素:
(1):散列函数是否均匀
(2):处理冲突的方法
(3):散列表的装填因子a