`
凤凰山
  • 浏览: 147574 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

散列表(哈希表)工作原理 .

 
阅读更多

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 为存储的数组。初始化比较容易,例如:

  1. const empty=maxlongint; // 用非常大的整数代表这个位置没有存储元素   
  2. p=9997; // 表的大小   
  3. procedure makenull;  
  4. var i:integer;  
  5. begin  
  6. for i:=0 to p-1 do  
  7. A[i]:=empty;  
  8. End;   


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

  1. function h(x:longint):Integer;  
  2. begin  
  3. h:= x mod p;  
  4. end;   


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

  1. function locate(x:longint):integer;  
  2. var orig,i:integer;  
  3. begin  
  4. orig:=h(x);  
  5. i:=0;  
  6. while (i<S)and(A[(orig+i)mod S]<>x)and(A[(orig+i)mod S]<>empty) do  
  7. inc(i);  
  8. //当这个循环停下来时,要么找到一个空的存储单元,要么找到这个元   
  9. //素存储的单元,要么表已经满了   
  10. locate:=(orig+i) mod S;  
  11. end;   


插入元素

  1. procedure insert(x:longint);  
  2. var posi:integer;  
  3. begin  
  4. posi:=locate(x); //定位函数的返回值   
  5. if A[posi]=empty then A[posi]:=x  
  6. else error; //error 即为发生了错误,当然这是可以避免的   
  7. end;   


查找元素是否已经在表中

  1. procedure member(x:longint):boolean;  
  2. var posi:integer;  
  3. begin  
  4. posi:=locate(x);  
  5. if A[posi]=x then member:=true  
  6. else member:=false;  
  7. 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 的约数越多,发生这种余数分布不均匀的情况就越频繁,冲突的几率越高。而素数的约数是最少的,因此我们选用大素数。记住“素数是我们的得力助手”。
       另一方面,一味的追求低冲突率也不好。理论上,是可以设计出一个几乎完美,几乎没有冲突的函数的。然而,这样做显然不值得,因为这样的函数设计很浪费时间而且编码一定很复杂,与其花费这么大的精力去设计函数,还不如用一个虽然冲突多一些但是编码简单的函数。因此,函数还需要易于编码,即易于实现。
       综上所述,设计一个好的哈希函数是很关键的。而“好”的标准,就是较低的冲突率和易于实现。
       另外,使用哈希表并不是记住了前面的基本操作就能以不变应万变的。有的时候,需要按照题目的要求对哈希表的结构作一些改进。往往一些简单的改进就可以带来巨大的方便。
这些只是一般原则,真正遇到试题的时候实际情况千变万化,需要具体问题具体分析才行。

 

 

 

-------------

转自:http://blog.csdn.net/ilibaba/article/details/3960142

分享到:
评论

相关推荐

    哈希表哈希表哈希表.zip

    哈希表,也被称为散列表,是计算机科学中一种高效的数据结构,用于存储和检索数据。它通过将关键字(key)映射到一个特定的位置来实现快速访问,这个位置称为哈希码或哈希值。哈希表的核心原理是利用哈希函数将...

    c语言或c++课程设计之散列表哈希表

    本篇文章将深入探讨散列表哈希表的基本概念、工作原理以及如何使用C或C++实现。 一、散列表哈希表基础 1. 哈希函数:哈希函数是散列表的核心,它接受一个输入(通常是字符串或任何其他数据类型),并返回一个介于0...

    易语言闪电哈希表模块源码.zip易语言项目例子源码下载

    哈希表(Hash Table),又称为散列表,是数据结构中的一个重要概念,它提供了一种快速存取数据的方法,通过特定的哈希函数将关键字映射到数组的索引位置。 哈希表的核心在于它的哈希函数,这个函数能够将输入的...

    哈希表de 设计.rar_c++ 哈希表_哈希_哈希 VC_哈希表

    哈希表,也被称为散列表,是计算机科学中一种高效的数据结构,用于存储和检索数据。在C++中,哈希表通常通过自定义实现或使用标准库中的`unordered_map`来实现。本文将深入探讨哈希表的设计原理、工作方式以及在C++...

    散列表 (哈希表,线性探测再散列)

    ### 散列表(哈希表,线性探测再散列) #### 1. 散列表概念 散列表,又称哈希表,是一种基于数组结构的数据结构。它通过哈希函数将一组关键字映射到一个有限的连续地址集(即数组索引范围),并将这些关键字作为...

    易语言源码易语言哈希表对象源码.rar

    哈希表,又称为散列表,是计算机科学中常用的一种数据结构,用于高效地存储和查找数据。在易语言中,哈希表对象是一个关键的组成部分,它在程序设计中扮演着重要角色。 哈希表的基本原理是通过哈希函数将键(Key)...

    c语言面试题之哈希表赎金信.zip

    哈希表,又称散列表,是计算机科学中一种非常重要的数据结构,它通过关联键(key)和值(value)来实现快速的数据存取。在C语言面试中,哈希表赎金信的问题通常考察面试者对哈希表的理解、算法设计及优化能力。以下...

    数据结构哈希表实验报告.doc

    本次实验的主要目的是通过对哈希表的理解和应用,让学生掌握哈希表的基本原理及其在实际问题中的运用。哈希表是一种非常高效的数据结构,它允许快速地插入、删除和查找数据。在本实验中,学生将通过一个具体的场景...

    哈希表(散列表)原理详解 - CSDN博客1

    哈希表,又称散列表,是一种高效的数据存储和查找结构,其主要原理是通过散列函数将关键码值(Key value)映射到一个固定大小的数组中的特定位置,从而实现快速访问。这个映射过程使得我们可以直接根据键(Key)来...

    Java的哈希表数据结构.docx

    哈希表是一种高效的数据结构,它通过散列函数将键(key)映射到一个固定大小的数组(也称为散列表)中。在Java中,常见的哈希表实现是`java.util.HashMap`类。在提供的代码示例中,作者自定义了一个简单的哈希表`...

    易语言源码易语言js哈希表源码.rar

    哈希表,也被称为散列表,是数据结构的一种,它通过哈希函数将键(Key)映射到一个数组的索引位置,以达到快速查找的目的。在哈希表中,插入、查找和删除操作通常可以在平均时间复杂度为O(1)的情况下完成,因此哈希...

    c语言基础-c语言编程基础之哈希表编程示例.zip

    哈希表,又称散列表,是计算机科学中一种非常重要的数据结构,用于高效地存储和检索数据。在C语言中实现哈希表可以帮助我们快速执行查找、插入和删除操作,通常其时间复杂度可达到O(1)。下面将详细阐述哈希表的基本...

    使用哈希表来识别元素.rar_哈希_哈希表

    哈希表,也被称为散列表,是数据结构中一种高效且实用的工具,它通过将键(Key)映射到值(Value)的方式来存储和检索数据。哈希表的精髓在于它的快速查找能力,通常可以在平均时间复杂度为O(1)的情况下完成查找、...

    哈希表(散列表)和哈希查找

    哈希表,也称为散列表,是一种数据结构,它通过使用哈希函数将关键字映射到存储地址,从而实现高效的数据查找。哈希函数是关键所在,它将输入的关键字转换为存储位置,通常表示为 d = H(key)。哈希表本身是一个数组...

    哈希表设计 哈希表的具体实现代码

    哈希表,也被称为散列表,是数据结构中一种高效的数据存储方式,它通过特定的哈希函数将关键字映射到一个固定大小的数组中,从而实现快速的查找、插入和删除操作。在计算机科学中,哈希表的性能优势在于它的平均时间...

    数据结构 哈希表 哈希算法

    哈希表,也被称为散列表,是数据结构中一种高效的数据存储和检索工具。它通过哈希函数将数据的关键字映射到一个固定大小的数组中,使得在平均情况下,查找、插入和删除操作的时间复杂度可以达到O(1)。这种高效的性能...

    哈希表(散列表)

    这个PPT讲了哈希表的基本原理和应用,还有字符串匹配的应用。

    sanliebiao.rar_hash text find_哈希值_哈希检索_哈希表_文件哈希表

    1. 哈希表原理:哈希表使用哈希函数将输入(通常是字符串,如文件名或单词)转化为数组索引,从而将数据存储在数组中。这样,当我们需要查找特定元素时,只需再次应用哈希函数,快速定位到对应的数组位置。 2. 哈希...

Global site tag (gtag.js) - Google Analytics