散列的概念
散列技术、散列表、散列函数、散列地址
所谓的查找实际上就是要确定关键码等于给定值的记录在查找结构中的存储位置。理想情况就是不经过任何比较,直接便能得到待查记录的存储位置,那就必须在记录的存储位置与它的关键码之间确立一个对应的关系 H H H,使得每个关键码 key 和唯一的存储位置 H ( k e y ) H(key) H(key) 相对应。在查找时,根据这个确定的对应关系找到给定值 k k k 的映射 H ( k ) H(k) H(k) ,若集合中存在这个记录,则必定在 H ( k ) H(k) H(k) 位置上,这种查找技术叫做 散列技术。采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间叫做 散列表,将关键码映射为散列表中适当存储位置的函数称为 散列函数,所得的存储位置叫做 散列地址。
冲突、同义词
当两个不同的记录需要存放在同一个位置时,即 H ( k 1 ) = H ( k 2 ) H(k_1) = H(k_2) H(k1)=H(k2),这种现象称为 冲突,此时 k 1 k_1 k1 和 k 2 k_2 k2 相对于 H H H 称为 同义词。
散列函数的设计
- 直接定址法: H ( k e y ) = a × k e y + b , ( a , b 为 常 数 ) H(key) = a \times key + b,(a,b为常数) H(key)=a×key+b,(a,b为常数)
- 除数留余法: H ( k e y ) = k e y % p H(key) = key \% p H(key)=key%p,通常选p为小于过等于表长的最小素数或不包含小于20质因子的合数
- 数字分析法:通过分析关键码来取分配散列地址
- 平方取中法:对关键字取平方后,按散列表大小,取中间的若干位作为散列地址(平方后截取)
处理冲突的方法
-
开放定址法
用开放定址法处理冲突得到的散列表叫做闭散列表。有三种常用的开放定址法。
①线性探索法:当发生冲突时,从冲突的下一个位置起,依次往后寻找空的散列地址。
②二次探测法:当发生冲突时,用 H i = ( H ( k e y ) + d i ) % m , ( d i = 1 2 , − 1 2 , 2 2 , − 2 2 , . . . , q 2 , − q 2 且 q < = m ) H_i = (H(key) + d_i) \% m,(d_i = 1^2,-1^2,2^2,-2^2,...,q^2,-q^2且q <= \sqrt{m}) Hi=(H(key)+di)%m,(di=12,−12,22,−22,...,q2,−q2且q<=m)查找空的散列地址
③随机探测法:当发生冲突时,用 H i = ( H ( k e y ) + d i ) % m , ( d i = 一 个 1 到 m 的 随 机 数 序 列 ) H_i = (H(key) + d_i) \% m,(d_i=一个1到m的随机数序列) Hi=(H(key)+di)%m,(di=一个1到m的随机数序列)
-
拉链法
用拉链法处理冲突构造的散列表叫做开散列表。
拉链法的基本思想是:将所有散列地址相同的记录,即所有关键码为同义词的记录存储在一个单列表中——称为同义词子表,在散列表中存储的是所有同义词子表的头指针。
散列表分析
散列表查找的性能分析
在查找过程中,关键码的比较次数取决于产生冲突的概率。产生的冲突越多,查找效率就越低。影响冲突产生的概率有以下三个因素:
-
散列表是否均匀
散列表是否均匀直接影响产生冲突的概率。
-
处理冲突的方法
处理冲突方法不同,它们的平均查找长度不同。容易看出,由于线性探索法处理冲突可能产生堆积,从而增加了平均查找长度;而拉链法处理冲突不会产生堆积。
-
散列表的填装因子
散 列 表 填 装 因 子 α = 填 入 表 中 的 记 录 个 数 / 散 列 表 的 长 度 散列表填装因子 \alpha =填入表中的记录个数/散列表的长度 散列表填装因子α=填入表中的记录个数/散列表的长度
填装因子 α \alpha α就表示散列表装满的程度。填装因子越大,发生冲突的可能性就越大
开散列表与闭散列表的比较
- 开散列表是用链表方法存储同义词,不产生堆积现象,且使得动态查找的查找、插入和删除等基本操作易于实现。其平均查找长度较短,但由于附加指针域而增加了存储开销。
- 闭散列表无需附加指针,因而存储效率较高。但由此带来的问题是容易产生堆积现象使得查找效率降低,而且由于空闲位置是查找不成功的条件,实现删除操作时不能简单地将待删记录清空,否则将截断该记录后继散列表地查找路径。因此闭散列表上删除只能是在待删记录所在单元做上记号,仅当运行到一定阶段时经过整理,才能真正删除有标记地单元。