哈希表工作原理 (并不特指Java中的HashTable)

1. 引言 
       哈希表(Hash Table)的应用近两年才在NOI中出现,作为一种高效的数据结构,它正在竞赛中发挥着越来越重要的作用。 
哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。 
       哈希表又叫做散列表,分为“开散列” 和“闭散列”。考虑到竞赛时多数人通常避免使用动态存储结构,本文中的“哈希表”仅指“闭散列”,关于其他方面读者可参阅其他书籍。

2. 基础操作 
2.1 基本原理 
       我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数,
也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一
个元素“分类”,然后将这个元素存储在相应“类”所对应的地方。 
但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了“冲突”,换句话说,就是把不同的元素分在了相同的“类”之中。后面我们将看到一种解决“冲突”的简便做法。 
总的来说,“直接定址”与“解决冲突”是哈希表的两大特点。

2.2 函数构造 
       构造函数的常用方法(下面为了叙述简洁,设 h(k) 表示关键字为 k 的元素所对应的函数值): 
a) 除余法: 
       选择一个适当的正整数 p ,令 h(k ) = k mod p ,这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。 
b) 数字选择法: 
       如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。

2.3 冲突处理 
       线性重新散列技术易于实现且可以较好的达到目的。令数组元素个数为 S ,则当 h(k) 已经存储了元素的时候,依次探查
(h(k)+i) mod S , i=1,2,3……
,直到找到空的存储单元为止(或者从头到尾扫描一圈仍未发现空单元,这就是哈希表已经满了,发生了错误。当然这是可以通过扩大数组范围避免的)。

2.4 支持运算 
       哈希表支持的运算主要有:初始化(makenull)、哈希函数值的运算(h(x))、插入元素(insert)、查找元素(member)。 设插入的元素的关键字为 x ,A 为存储的数组。 初始化比较容易,例如 :

[cpp] view
plain
copy

  1. const
  2. fordo

    End;

哈希函数值的运算根据函数的不同而变化,例如除余法的一个例子:

[cpp] view
plain
copy

  1. function h(x:longint):Integer;
  2. end;

我们注意到,插入和查找首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位置,因此加入一个定位的函数 locate

[cpp] view
plain
copy

  1. function locate(x:longint):integer;
  2. whiledo

    //当这个循环停下来时,要么找到一个空的存储单元,要么找到这个元
    //素存储的单元,要么表已经满了

    end;

插入元素

[cpp] view
plain
copy

  1. procedure insert(x:longint);
  2. if
    else
  3. end;

查找元素是否已经在表中

[cpp] view
plain
copy

  1. procedure member(x:longint):boolean;
  2. iftrue
    elsefalse
    end;

这些就是建立在哈希表上的常用基本运算。

初步结论: 
      
当数据规模接近哈希表上界或者下界的时候,哈希表完全不能够体现高效的特点,甚至还不如一般算法。但是如果规模在中央,它高效的特点可以充分体现。试验表
明当元素充满哈希表的 90%
的时候,效率就已经开始明显下降。这就给了我们提示:如果确定使用哈希表,应该尽量使数组开大,但对最太大的数组进行操作也比较费时间,需要找到一个平衡
点。通常使它的容量至少是题目最大需求的 120% ,效果比较好(这个仅仅是经验,没有严格证明)。

4. 应用举例 
4.1 应用的简单原则 
       什么时候适合应用哈希表呢?如果发现解决这个问题时经常要询问:“某个元素是否在已知集合中?”,也就是需要高效的数据存储和查找,则使用哈希表是最好不过的了!那么,在应用哈希表的过程中,值得注意的是什么呢? 
哈希函数的设计很重要。一个不好的哈希函数,就是指造成很多冲突的情况,从前面的例子已经可以看出来,解决冲突会浪费掉大量时间,因此我们的目标
就是尽力避免冲突。前面提到,在使用“除余法”的时候,h(k)=k mod p ,p 最好是一个大素数。这就是为了尽力避免冲突。为什么呢?假设
p=1000 ,则哈希函数分类的标准实际上就变成了按照末三位数分类,这样最多1000类,冲突会很多。一般地说,如果 p
的约数越多,那么冲突的几率就越大。 
简单的证明:假设 p 是一个有较多约数的数,同时在数据中存在 q 满足 gcd(p,q)=d >1 ,即有 p=a*d , q=b*d,
则有 q mod p= q – p* [q div p] =q – p*[b div a] . ① 其中 [b div a ]
的取值范围是不会超过 [0,b] 的正整数。也就是说, [b div a] 的值只有 b+1 种可能,而 p 是一个预先确定的数。因此 ①
式的值就只有 b+1 种可能了。这样,虽然mod 运算之后的余数仍然在 [0,p-1] 内,但是它的取值仅限于
① 可能取到的那些值。也就是说余数的分布变得不均匀了。容易看出, p
的约数越多,发生这种余数分布不均匀的情况就越频繁,冲突的几率越高。而素数的约数是最少的,因此我们选用大素数。记住“素数是我们的得力助手”。 
      
另一方面,一味的追求低冲突率也不好。理论上,是可以设计出一个几乎完美,几乎没有冲突的函数的。然而,这样做显然不值得,因为这样的函数设计
很浪费时间而且编码一定很复杂,与其花费这么大的精力去设计函数,还不如用一个虽然冲突多一些但是编码简单的函数。因此,函数还需要易于编码,即易于实
现。 
       综上所述,设计一个好的哈希函数是很关键的。而“好”的标准,就是较低的冲突率和易于实现。 
       另外,使用哈希表并不是记住了前面的基本操作就能以不变应万变的。有的时候,需要按照题目的要求对哈希表的结构作一些改进。往往一些简单的改进就可以带来巨大的方便。 
这些只是一般原则,真正遇到试题的时候实际情况千变万化,需要具体问题具体分析才行。

哈希表工作原理 (并不特指Java中的HashTable)

时间: 2024-08-07 14:14:31

哈希表工作原理 (并不特指Java中的HashTable)的相关文章

哈希表工作原理

1. 引言        哈希表(Hash Table)的应用近两年才在NOI中出现,作为一种高效的数据结构,它正在竞赛中发挥着越来越重要的作用. 哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间:而代价仅仅是消耗比较多的内存.然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的.另外,编码比较容易也是它的特点之一.        哈希表又叫做散列表,分为“开散列” 和“闭散列”.考虑到竞赛时多数人通常避免使用动态存储结构,本文中的“哈希表”仅指“闭

哈希表的原理与实现

[转自]:http://my.oschina.net/chape/blog/132533 目录[-] 哈希表的原理与实现 一致性 hash 算法 基本场景 hash 算法和单调性 consistent hashing 算法的原理 虚拟节点 小结 分布式哈希算法 哈希函数 哈希表 分布式哈希表 哈希表的工作原理与常用操作 基础操作 应用举例 哈希表的原理与实现 一列键值对数据,存储在一个table中,如何通过数据的关键字快速查找相应值呢?不要告诉我一个个拿出来比较key啊,呵呵. 大家都知道,在所

Java中的HashTable详解

Hashtables提供了一个很有用的方法可以使应用程序的性能达到最佳. Hashtables(哈 希表)在计算机领域中已不 是一个新概念了.它们是用来加快计算机的处理速度的,用当今的标准来处理,速度非常慢,而它们可以让你在查询许多数据条目时,很快地找到一个特殊的条目. 尽管现代的机器速度已快了几千倍,但是为了得到应用程序的最佳性能,hashtables仍然是个很有用的方法. 设想一下,你有一个 包含约一千条记录的数据文件??比如一个小企业的客户记录还有一个程序,它把记录读到内存中进行处理.每个

查找--------哈希表的原理

这段时间 在 准备软件设计师考试    目的是想复习一下  自己以前没怎么学的知识    在这个过程中  有了很大的收获  对以前不太懂得东西  在复习的过程中  有了很大程度的提高 比如在复习 程序语言的时候    对编译程序的处理过程和文法分析 有了全新的了解 作为一个半路出家  没学过程序语言这门课的我来说   有一种醍醐灌顶的感觉   以前在看 javaweb技术内幕是 对里面提的javac 的编译原理  看的真的是 云里雾里   哈哈哈   想想 也是醉了  最基础的程序语言 都没有看

java中的HashTable,HashMap和HashSet

目录(?)[+] 上篇博客中我们详细的分析了java集合<java中Map,List与Set的区别>. 同时我们也对HashSet和HashMap的核心方法hashcode进行了详解,见<探索equals()和hashCode()方法>. 万事俱备,那么下面我们就对基于hash算法的三个集合HashTable,HashSet和HashMap详解. 本文目录: 1. HashTable和HashMap的区别 2. HashSet和HashMap的区别 3. HashMap,HashS

哈希表解决冲突链地址java

package ch17; import java.math.BigInteger; public class HashTable { private LinkList[] arr; /** * 默认的构造方法 */ public HashTable() { arr = new LinkList[100]; } /** * 指定数组初始化大小 */ public HashTable(int maxSize) { arr = new LinkList[maxSize]; } /** * 插入数据

哈希表数据结构原理

java中哈希表及其应用详解

哈希表也称为散列表,是用来存储群体对象的集合类结构. 什么是哈希表 数组和向量都可以存储对象,但对象的存储位置是随机的,也就是说对象本身与其存储位置之间没有必然的联系.当要查找一个对象时,只能以某种顺序(如顺序查找或二分查找)与各个元素进行比较,当数组或向量中的元素数量很多时,查找的效率会明显的降低. 一种有效的存储方式,是不与其他元素进行比较,一次存取便能得到所需要的记录.这就需要在对象的存储位置和对象的关键属性(设为 k)之间建立一个特定的对应关系(设为 f),使每个对象与一个唯一的存储位置

Java知多少(79)哈希表及其应用

哈希表也称为散列表,是用来存储群体对象的集合类结构. 什么是哈希表 数组和向量都可以存储对象,但对象的存储位置是随机的,也就是说对象本身与其存储位置之间没有必然的联系.当要查找一个对象时,只能以某种顺序(如顺序查找或二分查找)与各个元素进行比较,当数组或向量中的元素数量很多时,查找的效率会明显的降低. 一种有效的存储方式,是不与其他元素进行比较,一次存取便能得到所需要的记录.这就需要在对象的存储位置和对象的关键属性(设为 k)之间建立一个特定的对应关系(设为 f),使每个对象与一个唯一的存储位置