散列表/哈希表

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

emmm,代码码了一半,还有一些不太会,晚几天慢慢把它都理解透了再补上来。

你可能感兴趣的:(散列表/哈希表)