切换到宽版
  • 15920阅读
  • 16回复

『转帖』浅谈竞赛中哈希表的应用 [复制链接]

上一主题 下一主题
离线gelanjie
 
只看楼主 正序阅读 0 发表于: 2005-11-07
— 本帖被 stevenjl 从 资料教程 移动到本区(2007-08-12) —
浅谈竞赛中哈希表的应用(一)



                



[关键词] 应用 哈希表 数据结构

[摘要]

  哈希表是一种高效的数据结构。本文分五个部分:首先提出了哈希表的优点,其次介绍了它的基础操作,接着从简单的例子中作了效率对比,指出其适用范围以及特点,然后通过例子说明了如何在题目中运用哈希表以及需要注意的问题,最后总结全文。

[正文]

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 为存储的数组。
  初始化比较容易,例如
  const empty=maxlongint; // 用非常大的整数代表这个位置没有存储元素
     p=9997;      // 表的大小
  procedure makenull;
   var i:integer;
   begin
    for i:=0 to p-1 do
     A:=empty;
   End;

  哈希函数值的运算根据函数的不同而变化,例如除余法的一个例子:
  function h(x:longint):Integer;
   begin
    h:= x mod p;
   end;

  我们注意到,插入和查找首先都需要对这个元素定位,即如果这个元素若存在,它应该存储在什么位置,因此加入一个定位的函数 locate
  function locate(x:longint):integer;
   var orig,i:integer;
   begin
    orig:=h(x);
    i:=0;
    while (i<S)and(A[(orig+i)mod S]<>x)and(A[(orig+i)mod S]<>empty) do
     inc(i);
     //当这个循环停下来时,要么找到一个空的存储单元,要么找到这个元
     //素存储的单元,要么表已经满了
    locate:=(orig+i) mod S;
   end;
  插入元素
  procedure insert(x:longint);
   var posi:integer;
   begin
    posi:=locate(x);      //定位函数的返回值
    if A[posi]=empty then A[posi]:=x
          else error; //error 即为发生了错误,当然这是可以避免的
   end;

  查找元素是否已经在表中
  procedure member(x:longint):boolean;
    var posi:integer;
    begin
     posi:=locate(x);
     if A[posi]=x then member:=true
             else member:=false;
    end;

  这些就是建立在哈希表上的常用基本运算。
[ 此贴被arronking在2005-11-13 19:56重新编辑 ]
离线cjrzh
只看该作者 16 发表于: 2007-11-13
顶!!!!
离线prb1989
只看该作者 15 发表于: 2007-11-12
noip不会考的,这是noi的要求
离线然。伊
只看该作者 14 发表于: 2007-10-30
感谢~抱走慢慢看~
离线fish
只看该作者 13 发表于: 2007-10-27
好是好...

但是,看完了还是不大懂...
唉~~
悟性太浅...
离线clwxzh57
只看该作者 12 发表于: 2007-08-12
谢谢啊!
离线birdor
只看该作者 11 发表于: 2007-08-11
好货
离线yuyan
只看该作者 10 发表于: 2006-02-03
只可惜,我还不是很懂哈希表的应用,请各位指点一二,晚生感激不尽
离线youling
只看该作者 9 发表于: 2005-11-27
好东西!!!!!!!!
离线gelanjie
只看该作者 8 发表于: 2005-11-13
你没看到我是转贴吗?
快速回复
限100 字节
 
上一个 下一个