1. 程式人生 > >python數據結構與算法 29-1 哈希查找

python數據結構與算法 29-1 哈希查找

range 一個 常數 們的 rem 中間 數據 範圍 for

前面的章節中,我們利用數據集中元素的相對位置信息來提高查找算法的性能。

比方知道列表是有序的,能夠使用二分查找。本節我們走得更遠一些,創建一個數據結構,使得查找性能提高到O(1)。稱為哈希查找。

要做到這種性能,我們要知道元素的可能位置。假設每一個元素就在他應該在的位置上,那麽要查找的時候僅僅須要一次比較得到有沒有的答案,但以下將會看到。不是這麽回事。

哈希表是這樣一種數據集合,元素的保存的時候就存在easy找到位置上。哈希表表中每個位置,一般稱為槽位,每個槽位都能保存一個數據元素並以一個整數命名(0開始)。這樣我們就有0號槽位。1號槽位等等。起始時。哈希表裏沒有數據,槽位是空的。這樣在構建哈希表的時候,能夠把槽位值都初始化為

None,圖4顯示一個大小為11的哈希表,或者是說有m個槽位的哈希表。m010.

技術分享

技術分享

圖中元素和保存的槽位之間的映射關系,稱為哈希函數,哈希函數接受一個元素作為參數,返回一個0m-1的整數作為槽位名。假如我們有一個整數集542693177731,我們的第一個哈希函數就能夠用“余數法”。簡單地將元素除以表的大小,返回余數作為哈希值。(

h(item)=item%11)。表4是上述整數集的哈希值。

Table 4: Simple Hash Function Using Remainders

Item

Hash Value

54

10

26

4

93

5

17

6

77

0

31

9


註意余數法一般以某種形式存於全部哈希函數中。由於它的結果一定在槽位範圍內。

一旦哈希值計算出來,就要把元素插入到哈希表中指定的位置。

如圖5所看到的,註意6槽位和11槽位是空的,這就要引入滿載因子的概念,一般表述為:

λ=元素數量/哈希表容量

技術分享

這裏。就是


λ=6/11

如今當我們要查找的時候,僅僅要簡單地用哈希函數計算出槽位值。然後到表中檢查是否存在就能夠了,這個查找動作是O(1),由於計算哈希值的時間。以及到表中查找的時間是個常數。假設每件東西都各守其位。我們就發現了一個常數級的查找算法。

或許你已經註意到,這個技術僅在每一個元素相應一個位置時有效,比如,上面的樣例中假設添加一個44。那麽它的哈希值是0。可是77的值也是0。這時問題就出來了。2個值相應同一個槽位,這被稱為“collision”,非常明顯,collision給哈希技術造成了困難,我們隨後具體討論。


哈希函數

對給定的數據集,哈希函數將每一個元素映射為單個的槽位。稱為“完美哈希函數”,假設我們知道元素和集合固定不變。那麽構造一個完美哈希函數或許是可能的。壞消息是對一個隨意數據集合,沒有一個系統的方法來構造完美哈希函數,好消息是。哈希函數不完美也能提供不錯的性能。


假設一定要完美的哈希函數,一種方法是做大哈希表,以保證每一個元素都有自己的索引。

盡管在數據不多的情況下可行,可是假設數據非常大就不可行。

比方,假設數據項是8位號碼,這就須要十億個槽位。要是我們只用來保存25個學生的號碼,就太費了。

我們的目標是:collision最少,計算簡單,分布均勻。有幾種擴展余數法的方案,以下討論當中幾個。

折疊法:這樣的方法把元素分成相等的幾片(最後一片可能不相等)。然後再把碎片拼起來作為哈希值。比方我們的數據項是號碼436-555-4601,那麽應該把號碼分成2個一組,然後加起來,即43+65+55+46+01,得到210 。如果哈希表有11個槽位。那麽再一步用11除210來得到槽位。即210%11=1。所以號碼436-555-4601的哈希值是1 。有些折疊法多了一步,在相加之前,把數據位順序反轉,在上面的樣例中。即 43+56+55+64+01=219 計算219 % 11=10。

還有一種算法叫做“平方取中法”,先計算元素的平方值,再從中提取幾位數字。比如,對元素44。先計算442=1936。提取中間兩位93,然後再取余數法,得到5(93%11=5)

Table 5: Comparison of Remainder and Mid-Square Methods

表5 余數法與平方取中法的比較

Item

Remainder

Mid-Square

54

10

3

26

4

7

93

5

9

17

6

8

77

0

4

31

9

6

對於字符類元素也能創建哈希函數,單詞cat能夠看成一個數字串

>>> ord(‘c‘)
99
>>> ord(‘a‘)
97
>>> ord(‘t‘)
116

我們把這三個數字加起來,用余數法計算哈希值。

以下是一個計算字符串哈希值的函數:

Listing 1

defhash(astring, tablesize):
    sum=0
    for pos inrange(len(astring)):
        sum=sum+ord(astring[pos])
 
    returnsum%tablesize
技術分享

有意思的是,上述算法中。同樣字母不同順序的單詞得到的哈希值相等,解決的方法是加上字母的位置作為重量。圖7顯示了使用位置作為重量因子。改動後的哈希函數作為練習。

你也能夠思考幾種計算哈希值的方法,但必需要記住,哈希函數必需要簡單高效。不能成為計算的主要負擔。假設哈希函數太復雜,計算槽位名的時間超過了簡單的順序查找或二分查找的時間。那麽哈希函數還有什麽意義呢?




python數據結構與算法 29-1 哈希查找