哈希表及哈希表查找相关概念

来源:互联网 发布:速达软件数据库密码 编辑:程序博客网 时间:2024/04/30 11:32

原文地址:http://www.cnblogs.com/Bob-FD/archive/2012/10/16/2725916.html

1. 哈希表的概念
   对于动态查找表而言,1) 表长不确定;2)在设计查找表时,只知道关键字所属范围,而不知道确切的关键字。因此,一般情况需建立一个函数关系,以f(key)作为关键字为key的录在表中的位置,通常称这个函数f(key)为哈希函数。(注意:这个函数并不一定是数学函数)

 1. 哈希函数是一个映象,即:将关键字的集合映射到某个地址集合上,它的设置很灵活,只要这个地址集合的大小不超出允许范围即可;
 2. 由于哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突”现象,即:key1 key2,而 f(key1) = f(key2) 并且,改进哈希函数只能减少冲突,而不能避免冲突。
      因此,在设计哈希函数时,一方面要考虑选择一个“好”的哈希函数;另一方面要选择一种处理冲突的方法。所谓“好”的哈希函数,指的是对于集合中的任意一个关键字,经哈希函数“映象”到地址集合中任何一个地址的概率是相同的,称这类哈希函数为“均匀的”哈希函数。
  根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映象到一个有限的地址连续的地址集(区间)上,并以关键字在地址集中的“象”作为相应记录在表中的存储位置,这种表被称为哈希表。哈希表是基于哈希函数建立的一种查找表。

2. 哈希函数的构造方法

  对数字的关键字可有下列哈希函数的构造方法,若是非数字关键字,则需先对其进行数字化处理。
(1) 直接定址法
  哈希函数为关键字的线性函数:
   H(key) = key 或者 H(key) = a key + b
  适用场合:地址集合的大小 = 关键字集合的大小 
(2)数字分析法
  假设关键字集合中的每个关键字都是由s位数字组成(k1, k2, …, kn),分析关键字集中的
  全体,并从中提取分布均匀的若干位或它们的组合作为地址。
  适用场合:能预先估计出全体关键字的每一位上各种数字出现的频度。
(3) 平方取中法
  若关键字的每一位都有某些数字重复出现频度很高的现象,则先求关键字的平方值,以通过
  “平方”扩大差别,同时平方值的中间几位受到整个关键字中各位的影响;
(4) 折叠法
  若关键字的位数特别多,则可将其分割成几部分,然后取它们的叠加和为哈希地址。可有
  移位叠加
间界叠加两种处理方法。
(5) 除留余数法
   H(key) = key MOD p p≤m (表长)
  关键问题是:如何选取 p ?
  p 应为不大于m 的质数或是不含20以下的质因子
  例如:key = 12, 39, 18, 24, 33, 21 时, 若取 p=9, 则使所有含质因子3的关键字均映射
     到地址0, 3, 6 上,从而增加了“冲突”的可能性。
(6) 随机数法
  H(key) = Random(key)实际造表时,采用何种构造哈希函数的方法取决于建表的关键字集合
   的情况(包括关键字的范围和形态),总的原则是使产生冲突的可能性降到尽可能地小。

3. 处理冲突的方法

  处理冲突的实际含义是:为产生冲突的地址寻找下一个哈希地址。
(1)开放定址法
  为产生冲突的地址H(key)求得一个地址序列:
  H0, H1, H2, …, Hs 1≤s≤m-1
  其中:H0 = H(key)
  Hi = ( H(key) + di ) MOD m i=1, 2, …,s
  增量 di 有三种取法:
  1) 线性探测再散列
   di = c i 最简单的情况 c=1
  2) 平方探测再散列
   di = 1², -1², 2², -2², …,
  3) 随机探测再散列
   di 是一组伪随机数列
   注意:增量di应具有“完备性”,即产生的Hi均不相同,且所产生的s(m-1)个Hi值能覆盖
       哈希表中所有的地址。要求:
   · 平方探测时的表长m必为4j+3的质数;
   · 随机探测时的m和di没有公因子。
(2)链地址法
   将所有哈希地址相同的记录都链接在同一链表中。
   线性探测容易产生二次聚集,链地址肯定不会产生二次聚集。一次聚集的产生主要取决于哈希函数,在哈希函数均匀的前提下,可以认为没有一次聚集。 

(3)再哈希法

        在同义词产生地址冲突时计算另一个哈希函数地址,直到冲突不再发生。这种方法不易产生“聚集”,但增加了计算时间。

4. 哈希表的查找

  查找过程和造表过程一致。假设采用开放定址处理冲突,则查找过程为:
  对于给定值K, 计算哈希地址 i = H(K) ,若r[i] = NULL 则查找不成功;
  若 r[i].key = K 则查找成功, 否则求下一地址Hi,直至r[Hi] = NULL (查找不成功)
  或r[Hi].key = K (查找成功)为止。
 
//--- 开放定址哈希表的存储结构 ---

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int hashsize[] = { 997, ... }; // 哈希表容量递增表,一个合适的素数序列
typedefstruct {
     ElemType *elem;// 数据元素存储基址, 动态分配数组
     intcount; // 当前数据元素个数
     intsizeindex; // hashsize[sizeindex]为当前容量
} HashTable;
 
 
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1 
 
 
Status SearchHash (HashTable H, KeyType K,int &p, int &c) {
// 在开放定址哈希表H中查找关键码为K的元素,若查找成功,以p指示待查数据元素在表中位置,并返回SUCCESS;否则,以p指示插入位置,并返回UNSUCCESS, c用以计冲突次数,其初值置零,供建表插入时参考
  p = Hash(K); // 求得哈希地址
  while( H.elem[p].key != NULLKEY && !EQ(K, H.elem[p].key))
// 该位置中填有记录并且关键字不相等
  collision(p, ++c);// 求得下一探查地址p
  if(EQ(K, H.elem[p].key))
    returnSUCCESS; // 查找成功,p返回待查数据元素位置
  elsereturn UNSUCCESS;// 查找不成功,p返回的是插入位置
} // SearchHash

 
通过调用查找算法实现了开放定址哈希表的插入操作。

?
1
2
3
4
5
6
7
8
9
10
Status InsertHash (HashTable &H, Elemtype e) {
// 查找不成功时插入数据元素e到开放定址哈希表H中,并返回OK;若冲突次数过大,则//重建哈希表
  c = 0;
  if( HashSearch ( H, e.key, p, c ) == SUCCESS )
    returnDUPLICATE; // 表中已有与e有相同关键字的元素
  elseif ( c < hashsize[H.sizeindex]/2 ) {// 冲突次数c未达到上限,(阀值c可调)
    H.elem[p] = e; ++H.count;return OK; // 插入e
     }
else RecreateHashTable(H); // 重建哈希表
} // InsertHash

可见,无论查找成功与否,ASL均不为零。

决定哈希表查找的ASL的因素:
1. 选用的哈希函数;
2. 选用的处理冲突的方法;
3. 哈希表饱和的程度,装载因子α=n/m 值的大小一般情况下,可以认为选用的哈希函数是“均匀”的,则在讨论ASL时,可以不考虑它的因素。哈希表的ASL是处理冲突方法和装载因子的函数。可以证明查找成功时有下列结果:


 
 
5. 哈希表的删除操作
  从哈希表中删除记录时,要作特殊处理,相应地要修改查找算法对静态查找表,有时也可能找到不发生冲突的哈希函数。 即此时的哈希表的ASL=0, 此类哈希函数为理想(perfect)的哈希函数

0 0
原创粉丝点击