日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 散列表(哈希表)工作原理 (转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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 plaincopyprint?
  • const?empty=maxlongint;?//?用非常大的整數代表這個位置沒有存儲元素 ??
  • p=9997;?//?表的大小 ??
  • procedure?makenull;??
  • var?i:integer;??
  • begin??
  • for?i:=0?to?p-1?do??
  • A[i]:=empty;??
  • End;???
  • const empty=maxlongint; // 用非常大的整數代表這個位置沒有存儲元素 p=9997; // 表的大小 procedure makenull; var i:integer; begin for i:=0 to p-1 do A[i]:=empty; End;
    哈希函數值的運算根據函數的不同而變化,例如除余法的一個例子:

    [cpp] view plaincopyprint?
  • function?h(x:longint):Integer;??
  • begin??
  • h:=?x?mod?p;??
  • end;???
  • function h(x:longint):Integer; begin h:= x mod p; end;
    ?????? 我們注意到,插入和查找首先都需要對這個元素定位,即如果這個元素若存在,它應該存儲在什么位置,因此加入一個定位的函數 locate

    [cpp] view plaincopyprint?
  • 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;???
  • 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;?
    插入元素

    [cpp] view plaincopyprint?
  • procedure?insert(x:longint);??
  • var?posi:integer;??
  • begin??
  • posi:=locate(x);?//定位函數的返回值 ??
  • if?A[posi]=empty?then?A[posi]:=x??
  • else?error;?//error?即為發生了錯誤,當然這是可以避免的 ??
  • end;???
  • procedure insert(x:longint); var posi:integer; begin posi:=locate(x); //定位函數的返回值 if A[posi]=empty then A[posi]:=x else error; //error 即為發生了錯誤,當然這是可以避免的 end;
    查找元素是否已經在表中

    [cpp] view plaincopyprint?
  • procedure?member(x:longint):boolean;??
  • var?posi:integer;??
  • begin??
  • posi:=locate(x);??
  • if?A[posi]=x?then?member:=true??
  • else?member:=false;??
  • end;???
  • procedure member(x:longint):boolean; var posi:integer; begin posi:=locate(x); if A[posi]=x then member:=true else member:=false; end;
    這些就是建立在哈希表上的常用基本運算。

    ?
    初步結論:
    ?????? 當數據規模接近哈希表上界或者下界的時候,哈希表完全不能夠體現高效的特點,甚至還不如一般算法。但是如果規模在中央,它高效的特點可以充分體現。試驗表明當元素充滿哈希表的 90% 的時候,效率就已經開始明顯下降。這就給了我們提示:如果確定使用哈希表,應該盡量使數組開大,但對最太大的數組進行操作也比較費時間,需要找到一個平衡點。通常使它的容量至少是題目最大需求的 120% ,效果比較好(這個僅僅是經驗,沒有嚴格證明)。


    3. 應用舉例
    3.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

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的散列表(哈希表)工作原理 (转)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。