雜湊表 Hash Table 雜湊概述

2021-09-13 22:52:14 字數 3699 閱讀 8469

雜湊表支援一種最有效的檢索方法:雜湊。

從根來上說,乙個雜湊表包含乙個陣列,通過特殊的索引值(鍵)來訪問陣列中的元素。

雜湊表的主要思想是通過乙個雜湊函式,在所有可能的鍵與槽位之間建立一張對映表。雜湊函式每次接受乙個鍵將返回與鍵相對應的雜湊編碼或雜湊值。鍵的資料型別可能多種多樣,但雜湊值的型別只能是整型。

計算雜湊值和在陣列中進行索引都只消耗固定的時間,因此雜湊表的最大亮點在於它是一種執行時間在常量級的檢索方法。當雜湊函式能夠保證不同的鍵生成的雜湊值互不相同時,就說雜湊錶能直接定址想要的結果。但這只是理想狀態,在實際運用過程中,能夠直接定址結果的情況非常少。

通常與各種各樣的鍵相比,雜湊表的條目數相應較少。因此,絕大多數的雜湊函式會將一些不同的鍵對映到表中相同的槽位上。當兩個鍵對映到乙個相同的槽位上時,它們就產生了衝突。乙個好的雜湊函式能最大限度的減少衝突,但衝突不能完全消除,我們仍要想辦法處理這些衝突。

鏈式雜湊表從根本上說是由一組鍊錶構成。每個鍊錶都可以看做是乙個「桶」,我們將所有的元素通過雜湊的方式放到具體的不同的桶中。插入元素時,首先將其鍵傳入乙個雜湊函式(該過程稱為雜湊鍵),函式通過雜湊的方式告知元素屬於哪個「桶」,然後在相應的煉表頭插入元素。查詢或刪除元素時,用同樣的方式先找到元素的「桶」,然後遍歷相應的鍊錶,直到發現我們想要的元素。因為每個「桶」都是乙個鍊錶,所以鏈式雜湊表並不限制包含元素的個數。然而,如果表變得太大,它的效能將會降低。

當雜湊表中兩個鍵雜湊到乙個相同的槽位時,這兩個鍵之間將會產生衝突。鏈式雜湊表解決衝突的方法非常簡單:當衝突發生時,它就將元素放到已經準備好的「桶」中。但這同樣會帶來乙個問題,當過多的衝突發生在同一槽位時,此位置的「桶」將會變得越來越深,從而造成訪問這個位置的元素所需要的時間越來越多。

例如用取餘法進行hash,一些不同資料會出現在同乙個位址上,比如說x=5,13 mod x=3,18 mod x=3,這兩個數(13,18)便產生了衝突。

為了解決衝突,可以使用掛鏈的形式,將衝突資料像掛鏈一樣掛在同一位置,將13放在5的位置,將18掛在13後面,如果要查18,先18 mod 5=3 找到3的位置,先看3位置掛鏈上的第乙個數是13,不等於18,繼續往後面找,直到找到18,如果找不到,說明沒有出現,按要求輸出即可。我們可以發現,這樣的儲存方式很像(或者就是)鍊錶的儲存方式,所以我們可以使用鍊錶來處理「掛鏈」的部分。這樣查詢的問題就解決了。

紫色部分來自部落格:

感覺上述的鏈就是「桶」

在理想情況下,我們希望所有的「桶"以幾乎同樣的速度增長,這樣它們就可以盡可能的保持小的容量和相同的大小。換句話說,我們的目標就是盡可能的均勻和隨機地分配表中的元素,這種情況在理論上稱為均勻雜湊,而在實際中,我們只能盡可能近似達到這種狀態。

如果想插入表中的元素數量遠大於表中的「桶『的數量,那麼即使是在乙個均勻雜湊的過程中,表的效能也會迅速下降。在這種情況下,表中所有的」桶「都變得越來越深。因此,我們必須要特別注意乙個雜湊表的負載因子,其定義為:

a=n / m

其中n是表中的元素的個數,m是桶的個數。在均勻雜湊的情況下,鏈式雜湊表的負載因子告訴我們表中的」桶「能裝下元素個數的最大值。

例如,有乙個鏈式雜湊表,其」桶「的數量是m=1699,元素的數量n=3198,其負載因子a=3198/1699=2。所以在這種情況下,當查詢元素時,可能每個」桶「裡面的元素個數不超過兩個。當有乙個表的負載因子小於1時,這個表每個位置所包含的元素不超過1個。當然,由於均勻雜湊是乙個理想的近似的情況,因此在實際情況中我們往往會檢索超過負載因子建議的數值。如何達到更接近於均勻雜湊的情況,最終取決於如何選擇雜湊函式。

選擇雜湊函式

乙個好的雜湊函式旨在均勻雜湊,也就是,盡可能以均勻和隨機的方式散布乙個雜湊表中的元素。定義乙個雜湊函式,它將鍵k對映到雜湊表中的位置x。x稱為k的雜湊編碼,正式的表述為:

h(k) = x

一般來說,大多數的雜湊方法都假設k為整數,這樣k能夠很容易地以數學方式修改,從而使得h能夠更均勻地將元素分布在表中。當k不是乙個整數時,我們也可以很容易的將它強制置轉換為整型。

如何強制轉換一組鍵,很大程度上取決於鍵本身的特點。所以,在乙個特定的應用中,盡可能地獲取鍵的特性尤為重要。例如,如果我們想對程式中的識別符號進行雜湊,會發現程式中有很多相似的字首和字尾,因為開發人員傾向於將變數宣告為類似sampleptr、******ptr和sentryptr的名字。我們可以將鍵嚴格按照鍵的開頭和結尾字元來強制轉換,但這顯然不是乙個好辦法,因為對於乙個k會有多個整數與之對應。另一方面,我們不妨隨機地從4個位置來選擇字元,然後隨機地改變它們的順序,並將它們封裝到乙個4位元組的整數中。要記住,無論用什麼樣的方法來強制轉換鍵,目的都是盡可能選擇乙個能將鍵均勻、隨機地分布到表中的雜湊函式。

取餘法一種簡單地將整型k對映到m槽位的雜湊方法是計算k除以m所得到的餘數。我們稱之為取餘法,正式的表述為:

h(k) = k mod m

如果有m=1699個位置,而要雜湊的鍵k = 25657,通過這種方法得到雜湊編碼為25657 mod 1699 = 172。通常情況下,要避免m的值為2的冪。這是因為假如m=2p   ,那麼h僅僅是k的p個最低階位(這句有點沒看懂)。通常我們選擇的m會是乙個素數,且不要太接近於2的冪,同時還要考慮儲存空間的限制和負載因子。

例如,如果我們想往乙個鏈式雜湊表中插入n=4500個左右的元素,會選擇m=1699(m是乙個介於210~211之間的素數)。由此可以計算出它的負載因子a=4500/1699約等於2.6,根據均勻雜湊表述,這說明表中每個「桶」大概能容納2~3個元素。

乘法

與取餘法不同的是乘法。它將整型k乘以乙個常數a(0h(k) = m(ka mod 1), 其中a約等於0.618

這個方法有個優點是,對於表中槽位個數m的選擇並不需要像取餘法中那麼慎重。例如:如果表有m=2000個位置,雜湊的鍵k=6341,那麼得到的雜湊編碼為2000x(6341x0.618 mod 1) = 2000x(3918.738 mod 1)=2000x0.738=1476。

在鏈式雜湊表中,如果期待插入的元素個數n不超過4500個,可以讓m=2250。這樣得到的負載因子a=4500/2250=2,根據均勻雜湊的規則,在每個「桶」中儲存的元素個數一般不超過兩個。同時,這個雜湊方法可以讓我們更加靈活地選擇m,以便獲取我們可以接受的最大「桶」深。

示例1列舉了乙個能夠較好的處理字串的雜湊函式。它通過一系列的位操作將鍵強制轉換為整數。所有這些整數都是通過取餘法得到的。這個雜湊函式針對雜湊字串執行的很好。

示例:乙個適用於處理字串的雜湊函式

/*hashpjw.c*/

#include "hashpjw.h"

unsigned int hashpjw(const void *key)

ptr++;

}/*在實際操作中,使用實際大小代替prime_tblsiz*/

return val % prime_tblsiz;

}

**:

hash table 雜湊表(雜湊表)

hash table 也叫雜湊表。涉及到雜湊函式 雜湊衝突處理的問題。雜湊函式是雜湊表的關鍵,是用來從值到雜湊表索引 存放位置 的對映。比如 要存放乙個學校學生的姓名,他們是amanda,jack,andy,tom,bob,black等等500個人。我們可以設計乙個雜湊表,容量為500,來存放這些學...

HashTable 雜湊表(雜湊表)

雜湊表 雜湊表 是根據關鍵字 key 而訪問在記憶體位置的資料結構。其方法是 它通過乙個關鍵值的函式將所需的資料對映到表中的位置來訪問資料,這個對映函式叫做雜湊函式,存放記錄的陣列叫做雜湊表 雜湊表 構造雜湊表的幾種方法 直接定址法 取關鍵字的某個線性函式為雜湊位址,hash key key 或 h...

HashTable雜湊表 雜湊表(雜湊桶)

處理雜湊衝突的開鏈法 拉鍊法 雜湊桶 使用素數做雜湊表的長度,可以降低雜湊衝突 素數表size t getnextprime size t num 素數表 雜湊桶的節點的定義 template struct hashnode 在插入前檢查容量,以及負載因子。不夠就進行增容,按素數表進行增,以減小雜湊...