雜湊表的實現和HashMap的原理

2022-05-03 11:48:15 字數 3495 閱讀 3713

雜湊衝突最常用的解決辦法有開放定址法和鏈位址

1、開放定址法

2、鏈位址法

上面所說的開發定址法的原理是遇到衝突的時候查詢順著原來雜湊位址查詢下乙個空閒位址然後插入,但是也有乙個問題就是如果空間不足,那他無法

處理衝突也無法插入資料,

因此需要裝填因子(插入資料/空間)<=1。

那有沒有一種方法可以解決這種問題呢?鏈位址法可以,鏈位址法的原理時如果遇到衝突,他就會在原位址新建乙個空間,然後以鍊錶結點的形式插入

,而我的雜湊演算法是h(key)=key mod 16,第乙個資料1的雜湊值f(1)=1,插入到1結點的後面,第二個資料12的雜湊值f(12)=12,插

入到12結點,第三個資料26的雜湊值f(26)=10,插入到10結點後面,第4個資料337,計算得到雜湊值是1,遇到衝突,但是依然只需要找到該1結點的最

後鏈結點插入即可,同理353。

1、在計算出雜湊值 h 之後,我們通常會用雜湊值去對雜湊表的長度length取模,使元素比較均勻的分布在雜湊表內。

2、還有一種效率更高的方法,就是用雜湊值去和雜湊表的初始長度length-1進行 與 運算,h & (length-1)就是元素在雜湊表內的位址

但是這種方法對雜湊表的長度有要求,只有雜湊表的初始長度是 2^n才可以,為什麼?

因為2^n-1的二進位制表示每一位都是1,跟雜湊值進行與運算的時候能產生更多不同的結果,從而降低衝突的可能

以雜湊表長度為4=2^2舉個例子:

看下圖,左邊兩組是陣列長度為16(2的4次方),右邊兩組是陣列長度為15。兩組的hashcode均為8和9,但是很明顯,當它們和1110「與」的時候,產生了相同的結果,

也就是說它們會定位到陣列中的同乙個位置上去,這就產生了碰撞,8和9會被放到同乙個鍊錶上,那麼查詢的時候就需要遍歷這個鍊錶,得到8或者9,這樣就降低了查詢的效率。

同時,我們也可以發現,當陣列長度為15的時候,hashcode的值會與14(1110)進行「與」,那麼最後一位永遠是0,而0001,0011,0101,1001,1011,0111,1101這幾個位置永遠都不能存放元素了,

空間浪費相當大,更糟的是這種情況中,陣列可以使用的位置比陣列長度小了很多,這意味著進一步增加了碰撞的機率,減慢了查詢的效率!

所以說,當陣列長度為2的n次冪的時候,不同的key算得得index相同的機率較小,那麼資料在陣列上分布就比較均勻,也就是說碰撞的機率小,相對的,查詢的時候就不用遍歷某個位置上的鍊錶,

這樣查詢效率也就較高了。

說到這裡,我們再回頭看一下hashmap中預設的陣列大小是多少,檢視源**可以得知是16,為什麼是16,而不是15,也不是20呢,看到上面annegu的解釋之後我們就清楚了吧,顯然是因為16是2的整數次冪的原因,在小資料量的情況下16比15和20更能減少key之間的碰撞,而加快查詢的效率。

所以,在儲存大容量資料的時候,最好預先指定hashmap的size為2的整數次冪次方。就算不指定的話,也會以大於且最接近指定值大小的2次冪來初始化的

c語言實現**

#include#include

#include

#define hashsize 10 //

雜湊表結點個數

using

namespace

std;

struct node//

定義乙個節點

;struct hashtable//

定義乙個雜湊表

table[hashsize];

//初始化雜湊表

void hashtable_init()//

雜湊表初始化

//雜湊函式(開放定址)

size_t hash(char*key)

//查詢鍵是否已經存在

node* find(char*key)

return

null;}//

插入結點建立對映關係

void insert(char* key,char*val)

//如果鍵已經被對映過,直接改變對應的值即可

new->val=val;

}void

print()

cout

}int

main()

print();

return0;

}

map的底層都是通過雜湊表進行實現的,那先來看看什麼是雜湊表。

jdk1.8之前,雜湊表底層採用陣列+鍊錶實現,即使用煉表處理衝突,同一hash值的鍊錶都儲存在乙個煉表裡。但是當位於乙個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查詢的效率較低。

jdk1.8中,雜湊表儲存採用陣列+鍊錶+紅黑樹實現,當鍊表長度超過閾值(8)時,將鍊錶轉換為紅黑樹,這樣大大減少了查詢時間。如下圖

當hashmap中的元素越來越多的時候,碰撞的機率也就越來越高(因為陣列的長度是固定的),所以為了提高查詢的效率,就要對hashmap的陣列進行擴容,陣列擴容這個操作也會出現在arraylist中,

所以這是乙個通用的操作,很多人對它的效能表示過懷疑,不過想想我們的「均攤」原理,就釋然了,而在hashmap陣列擴容之後,最消耗效能的點就出現了:

原陣列中的資料必須重新計算其在新陣列中的位置,並放進去,這就是resize。 

那麼hashmap什麼時候進行擴容呢?當hashmap中的元素個數超過陣列大小*loadfactor時,就會進行陣列擴容,loadfactor的預設值為0.75,

也就是說,預設情況下,陣列大小為16,那麼當hashmap中元素個數超過16*0.75=12的時候,就把陣列的大小擴充套件為2*16=32,即擴大一倍,然後重新計算每個元素在陣列中的位置,

而這是乙個非常消耗效能的操作,所以如果我們已經預知hashmap中元素的個數,那麼預設元素的個數能夠有效的提高hashmap的效能。比如說,我們有1000個元素new hashmap(1000),

但是理論上來講new hashmap(1024)更合適,不過上面annegu已經說過,即使是1000,hashmap也自動會將其設定為1024。 但是new hashmap(1024)還不是更合適的,

因為0.75*1000 < 1000, 也就是說為了讓0.75 * size > 1000, 我們必須這樣new hashmap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。 

以上**自

C STL中的雜湊表 hash map

需要hash map的原因 map底層實現是紅黑數,查詢的效率是o log n 但是hash map的底層實現是雜湊表,它的查詢效率是o 1 當資料很大的時候,比如100w的時候,map找的話要比較20次左右,但是hash map卻只需要比較一到兩次。所以hash map就需要了。在定義hash m...

雜湊表(雜湊表)的實現

雜湊函式直接用key size的形式,size為雜湊表的大小。衝突處理採用平方探測法,為保證可以探測到整個雜湊表空間,雜湊表大小設定為4k 3形式的素數。當雜湊表中的元素過多時會造成效能下降,這時應該倍增雜湊表的大小,重新計算原來雜湊表中每個元素在新的雜湊表中的位置。雜湊表的實現 hashtable...

雜湊表 雜湊表 的實現原理

雜湊表可以表述為,是一種可以根據關鍵字快速查詢資料的資料結構。通常情況下,不論雜湊表中資料有多少,增加,刪除,改寫資料的複雜度平均都是o 1 效率非常高。如果說每乙個資料它都對應著乙個固定的位置,那我們查詢特定乙個資料時,就可以直接檢視這個資料對應的位置是否存在資料。乙個形象的例子就是學生在教室中的...