在之前的(一)順序查找和(二)二分查找中我們都是基於數據在列表中存儲的索引位置查找的,本文所要說的是基於hash表的查找
【hash表】又名散列表,是一種根據關鍵碼尋找值的數據映射結構。哈希表的每個位置,通常稱爲槽,對應存儲一個項,由0開始,如圖所示是一個size爲10的哈希表,最初每個槽中沒有值,均爲None.
【hash函數】又稱爲散列函數,是數據值與哈希表之間的映射函數。哈希函數接收數據值,返回值結餘0和size-1之間的整數(槽名範圍內),在上述哈希表中,返回的哈希值就必須介於0-9之間。下面介紹一下常見的哈希函數:
(1)餘數法
將數據值除以表的大小size,所得餘數即爲該數據值對應的槽的位置
H(item)=item%size
將數據25,38,46,12,31按照餘數法插入到上述哈希表中,則有如下
25%10 | 5 |
38%10 | 8 |
46%10 | 6 |
12%10 | 2 |
31%10 | 1 |
餘數法存在的問題是,如果輸入數據86,餘數6,和46所在槽衝突。如果要避免衝突就要儘可能提供多點槽,這樣又存在資源利用率不高,因此下面幾種方法就是做到最大程度減少衝突
(2)分組求和法
將項劃分爲相等大小的塊(最後一塊可能不是相等大小)。然後將這些塊加在一起以求出散列值。例如,如果我們的項是電話號碼 436-555-4601
,我們將取出數字,並將它們分成2位數(43,65,55,46,01)
。43 + 65 + 55 + 46 + 01
,我們得到 210。我們假設哈希表有 11 個槽,那麼我們需要除以 11 。在這種情況下,210%11
爲 1,因此電話號碼 436-555-4601
散列到槽 1 。一些分組求和法會在求和之前每隔一個反轉。對於上述示例,我們得到 43 + 56 + 55 + 64 + 01 = 219
,其給出 219%11 = 10
(3)平方取中法
首先求數據值得平方,然後提取一部分數字結果。例如,如果項是 44,我們將首先計算 44^2 = 1,936
。通過提取中間兩個數字 93
,我們得到 5(93%11)
(4)利用ascii碼(字符串)
對於字符串,求其散列值可以將字符串理解爲一個字符構成的序列,而每個字符對應一個ascii值,因此字符串也可以理解爲ascii的序列。利於求H('cat'),在ascii碼中,
得出三個字符的ascii值後,相加99+97+116=312,再利用餘數法求出散列值312%10=2,見【代碼1】
值得注意的是,這種基於ascii碼的哈希函數對於字符組成相同的得出的散列值是一樣的,即同樣存在衝突問題,例如利用【代碼1】求出的‘team’和‘meat’相同。
解決方式:可採用字符位置座位權重,例如,cat可採用99*1+97*2+116*3==641,641%10=1,見【代碼2】
【代碼1】
#利用ascii碼(字符串) def hashSearchByAscii(str,tablesize): sum=0 for s in str: sum=sum+ord(s) return sum%tablesize print(hashSearchByAscii('cat',10))
【代碼2】
#利用ascii碼(字符串)+字符的位置權重 def hashSearchByAscii2(str,tablesize): sum=0 for i in range(len(str)): sum=sum+ord(str[i])*(i+1) return sum%tablesize print(hashSearchByAscii2('team',10)) print(hashSearchByAscii2('meat',10)) print(hashSearchByAscii2('cat',10))