[转]哈希表
本文在原帖基礎上稍作了一些修飾。原帖地址為http://blog.163.com/hai_zone/blog/static/26461137201203123308/
哈希表???
一、哈希表算法-哈希表的概念及作用
根據設定的哈希函數H(key)和處理沖突的方法將一組記錄的關鍵字映象到一個有限的連續的地址集(區間)上,并以關鍵字(原象)在地址集中的“象”作為記錄在地址集中的存儲位置,這種存放記錄的地址集(數組)便稱為哈希表,這一映象過程稱為哈希造表或散列,所得存儲位置稱哈希地址或散列地址?
也就是說,它通過把關鍵碼值映射到表(數組)中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。
哈希表是一個以空間換取時間的數據結構。它可以提供快速的插入操作和查找操作。第一次接觸哈希表時,它的優點多得讓人難以置信。不論哈希表中有多少數據,插入和刪除(有時包括側除)只需要接近常量的時間即0(1)的時間級。實際上,這只需要幾條機器指令。
對哈希表的使用者一一人來說,這是一瞬間的事。哈希表運算得非常快,在計算機程序中,如果需要在一秒種內查找上千條記錄通常使用哈希表(例如拼寫檢查器)哈希表的速度明顯比樹快,樹的操作通常需要O(N)的時間級。哈希表不僅速度快,編程實現也相對容易。
哈希表也有一些缺點它是基于數組的,數組創建后難于擴展,某些哈希表被基本填滿時,性能下降得非常嚴重,所以程序雖必須要清楚表中將要存儲多少數據(或者準備好定期地把數據轉移到更大的哈希表中,這是個費時的過程)。
而且,也沒有一種簡便的方法可以以任何一種順序〔例如從小到大〕遍歷表中數據項。如果需要這種能力,就只能選擇其他數據結構。
然而如果不需要有序遍歷數據,井且可以提前預測數據量的大小。那么哈希表在速度和易用性方面是無與倫比的。
哈希表到底有什么樣的優勢?
數據結構中,有個時間算法復雜度O(n)的概念來衡量某種算法在時間效率上的優劣。哈希表的理想算法復雜度為O(1),也就是說利用哈希表查找某個值,系統所使用的時間在理想情況下為定值,這就是它的優勢。那么哈希表是如何做到這一點的呢?
我們定義一個很大的有序數組,想要得到位于該數組第n個位置的值,它的算法復雜度為O(1)。哈希表利用哈希函數將需要存儲的內容的關鍵值轉換為這個有序數組中的某個值,在被存儲內容和有序數組之間建立了映射關系。這樣,下次我們對這個值進行查找時只要使用同一個哈希函數對關鍵值進行轉換,找到這個數組值就可以了。
如果還沒有明白是怎么回事的話,那我們來舉個例子。假設我們要做個存儲結構,需要存儲下來三國中的人物,以及他們的詳細信息。我們用他們的名字來作為存儲的關鍵值,例如:劉備,曹操,孫權,關羽,張飛......等等。這個時候我們如果想用一般的方法來查找這些英雄豪杰,需要遍歷整個存儲空間,如果這些英雄豪杰一共有n個,那么這時候的時間算法復雜度為O(n)。顯然如果n值很大,每次想要找到某個英雄的詳細信息就需要比較長的時間。
此時我們先定義一個大的有序結構數組HashValue[m],用來存放各位英雄豪杰的信息。然后編寫一個哈希函數ChangeToHashValue (name),函數的具體內容就不細說了,反正這個函數會將這些做為關鍵值的名字轉換為HashValue[m]中的某個下標值x。然后可以將英雄的信息放進HashValue[x]中去。這樣,可以將所有英雄的信息存儲起來。當查詢的時候再使用哈希函數ChangeToHashValue(name)得到這個下標值,這樣就很容易得到了這個英雄的信息。例如:ChangeToHashValue(劉備)為10,那么就將劉備存儲到HashValue [10]里面。當查詢的時候再次使用ChangeToHashValue(劉備)得到10,這個時候我們就可以很容易找到劉備的所有信息。在實際應用中如果我們想把所有的英雄豪杰都存儲進系統時,需要定義m>n。就是數組的大小要大于需要存儲的信息量,所以說哈希表是一個以空間換取時間的數據結構。
這個時候問題來了,出現了這種情況ChangeToHashValue(關羽)和ChangeToHashValue(張飛)得到的值是一樣的,都是 250,我們豈不是在存儲過程中會遇到麻煩,怎么安排他們二位的地方呢?(總不能讓二位打一架,誰贏了誰呆在那吧),這就需要一個解決沖突的方法。當遇到這種情況時我們可以這樣處理,先存儲好了關羽,當張飛進入系統時會發現關羽已經是250了,那咱就加一位,251得了,這不就解決了。我們查找張飛的時候也是,一看250不是張飛,那就加個1,就找到了。這時還存在一個問題。直接用ChangeToHashValue(趙云)為251,張飛已經早早占了他的地方,那就再加1存到252唄。呵呵,這時我們會發現,當哈希函數沖突發生的機率很高時,可能會有一群英雄豪杰在250這個值后面扎堆排隊。要命的是查找的時候,時間算法復雜度早已不是O(1)了(所以我們說理想情況下哈希表的時間算法復雜度為O(1))。 這就是說哈希函數的編寫是哈希表的一個關鍵問題,會涉及到一個存儲值在哈希表中的統計分布。如果哈希函數已經定義好了,沖突的解決就成為了改變系統性能的關鍵因素。其實還有很多種方法來解決沖突情況下的存儲和查找問題,不一定非要線性向后排隊,如果有好的哈希表沖突的解決方法也能很大程度上提高系統的效率。?
一般的線性表,樹中,記錄在結構中的相對位置是隨機的,即和記錄的關鍵字之間不存在確定的關系,因此,在結構中查找記錄時需進行一系列和關鍵字的比較。這一類查找方法建立在“比較“的基礎上,查找的效率依賴于查找過程中所進行的比較次數。
理想的情況是能直接找到需要的記錄,因此必須在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系f,使每個關鍵字和結構中一個唯一的存儲位置相對應。
哈希表最常見的例子是以學生學號為關鍵字的成績表,1號學生的記錄位置在第一條,10號學生的記錄位置在第10條...
如果我們以學生姓名為關鍵字,如何建立查找表,使得根據姓名可以直接找到相應記錄呢?
?
?
哈希表算法
用上述得到的數值作為對應記錄在表中的位置,得到下表:
?
哈希表算法上面這張表即哈希表。
如果將來要查李秋梅的成績,可以用上述方法求出該記錄所在位置:
李秋梅:lqm 12+17+13=42 取表中第42條記錄即可。
問題:如果兩個同學分別叫 劉麗 劉蘭 該如何處理這兩條記錄?
這個問題是哈希表不可避免的,即沖突現象:對不同的關鍵字可能得到同一哈希地址。
二、哈希表算法-哈希表的構造方法
1、直接定址法
例如:有一個從1到100歲的人口數字統計表,其中,年齡作為關鍵字,哈希函數取關鍵字自身。
?????????? 但這種方法效率不高,時間復雜度是O(1),空間復雜度是O(n),n是關鍵字的個數
?
哈希表算法?
2、數字分析法
有學生的生日數據如下:
年.月.日
75.10.03
75.11.23
76.03.02
76.07.12
75.04.21
76.02.15
...
經分析,第一位,第二位,第三位重復的可能性大,取這三位造成沖突的機會增加,所以盡量不取前三位,取后三位比較好。
3、平方取中法
取關鍵字平方后的中間幾位為哈希地址。
4、折疊法
將關鍵字分割成位數相同的幾部分(最后一部分的位數可以不同),然后取這幾部分的疊加和(舍去進位)作為哈希地址,這方法稱為折疊法。
例如:每一種西文圖書都有一個國際標準圖書編號,它是一個10位的十進制數字,若要以它作關鍵字建立一個哈希表,當館藏書種類不到10,000時,可采用此法構造一個四位數的哈希函數。如果一本書的編號為0-442-20586-4,則:
?
哈希表算法?
5、除留余數法
取關鍵字被某個不大于哈希表表長m的數p除后所得余數為哈希地址。
H(key)=key MOD p (p<=m)
6、隨機數法
選擇一個隨機函數,取關鍵字的隨機函數值為它的哈希地址,即
H(key)=random(key) ,其中random為隨機函數。通常用于關鍵字長度不等時采用此法。
?
三、哈希表算法-處理沖突的方法
如果兩個同學分別叫 劉麗 劉蘭,當加入劉蘭時,地址24發生了沖突,我們可以以某種規律使用其它的存儲位置,如果選擇的一個其它位置仍有沖突,則再選下一個,直到找到沒有沖突的位置。選擇其它位置的方法有:
1、開放定址法
Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1)
其中m為表長,di為增量序列
如果di值可能為1,2,3,...m-1,稱線性探測再散列。
如果di取值可能為1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2)
稱二次探測再散列。
如果di取值可能為偽隨機數列。稱偽隨機探測再散列。
例:在長度為11的哈希表中已填有關鍵字分別為17,60,29的記錄,現有第四個記錄,其關鍵字為38,由哈希函數得到地址為5,若用線性探測再散列,如下:
?
?
2、再哈希法
當發生沖突時,使用第二個、第三個、哈希函數計算地址,直到無沖突時。缺點:計算時間增加。
3、鏈地址法
將所有關鍵字為同義詞的記錄存儲在同一線性鏈表中。
4、建立一個公共溢出區
假設哈希函數的值域為[0,m-1],則設向量HashTable[0..m-1]為基本表,另外設立存儲空間向量OverTable[0..v]用以存儲發生沖突的記錄。
?
轉載于:https://www.cnblogs.com/KingOfFreedom/archive/2012/12/11/2812505.html
總結
- 上一篇: 雷军鸽了四年的手机终于来了?有厂商想“复
- 下一篇: 金山试题