日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

哈希表数据结构_算法与数据结构-哈希表

發布時間:2025/3/12 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 哈希表数据结构_算法与数据结构-哈希表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前面我們已經講到了數組和鏈表,數組能通過下標 O(1) 訪問,但是刪除一個中間元素卻要移動其他元素,時間 O(n)。 循環雙端鏈表倒是可以在知道一個節點的情況下迅速刪除它,但是吧查找又成了 O(n)。

難道就沒有一種方法可以快速定位和刪除元素嗎?似乎想要快速找到一個元素除了知道下標之外別無他法,于是乎聰明的計算機科學家又想到了一種方法。 能不能給每個元素一種『邏輯下標』,然后直接找到它呢,哈希表就是這種實現。它通過一個哈希函數來計算一個元素應該放在數組哪個位置,當然對于一個 特定的元素,哈希函數每次計算的下標必須要一樣才可以,而且范圍不能超過給定的數組長度。

我們還是以書中的例子說明,假如我們有一個數組 T,包含 M=13 個元素,我們可以定義一個簡單的哈希函數 h

h(key) = key % M

這里取模運算使得 h(key) 的結果不會超過數組的長度下標。我們來分別插入以下元素:

765, 431, 96, 142, 579, 226, 903, 388

先來計算下它們應用哈希函數后的結果:

?

下邊我畫個圖演示整個插入過程(純手工繪制,原諒我字寫得不太優雅):

?

哈希沖突 (collision)

這里到插入 226 這個元素的時候,不幸地發現 h(226) = h(96) = 5,不同的 key 通過我們的哈希函數計算后得到的下標一樣, 這種情況成為哈希沖突。怎么辦呢?聰明的計算機科學家又想到了辦法,其實一種直觀的想法是如果沖突了我能不能讓數組中 對應的槽變成一個鏈式結構呢?這就是其中一種解決方法,叫做 鏈接法(chaining)。如果我們用鏈接法來處理沖突,后邊的插入是這樣的:

?

這樣就用鏈表解決了沖突問題,但是如果哈希函數選不好的話,可能就導致沖突太多一個鏈變得太長,這樣查找就不再是 O(1) 的了。 還有一種叫做開放尋址法(open addressing),它的基本思想是當一個槽被占用的時候,采用一種方式來尋找下一個可用的槽。 (這里槽指的是數組中的一個位置),根據找下一個槽的方式不同,分為:

  • 線性探查(linear probing): 當一個槽被占用,找下一個可用的槽。h(k,i)=(h′(k)+i)%m,i=0,1,...,m?1h(k,i)=(h′(k)+i)%m,i=0,1,...,m?1
  • 二次探查(quadratic probing): 當一個槽被占用,以二次方作為偏移量。 h(k,i)=(h′(k)+c1+c2i2)%m,i=0,1,...,m?1h(k,i)=(h′(k)+c1+c2i2)%m,i=0,1,...,m?1
  • 雙重散列(double hashing): 重新計算 hash 結果。 h(k,i)=(h1(k)+ih2(k))%mh(k,i)=(h1(k)+ih2(k))%m

我們選一個簡單的二次探查函數 h(k,i)=(home+i2)%mh(k,i)=(home+i2)%m,它的意思是如果 遇到了沖突,我們就在原始計算的位置不斷加上 i 的平方。我寫了段代碼來模擬整個計算下標的過程:

?
這段代碼輸出的結果如下:

?
遇到沖突之后會重新計算,每個待插入元素最終的下標就是:

?

Cpython 如何解決哈希沖突

如不同 cpython 版本實現的探查方式是不同的,后邊我們自己實現 HashTable ADT 的時候會模仿這個探查方式來解決沖突。

?

哈希函數

到這里你應該明白哈希表插入的工作原理了,不過有個重要的問題之前沒提到,就是 hash 函數怎么選? 當然是散列得到的沖突越來越小就好啦,也就是說每個 key 都能盡量被等可能地散列到 m 個槽中的任何一個,并且與其他 key 被散列到哪個槽位無關。 如果你感興趣,可以閱讀后邊提到的一些參考資料。視頻里我們使用二次探查函數,它相比線性探查得到的結果沖突會更少。

裝載因子(load factor)

如果繼續往我們的哈希表里塞東西會發生什么?空間不夠用。這里我們定義一個負載因子的概念(load factor),其實很簡單,就是已經使用的槽數比哈希表大小。 比如我們上邊的例子插入了 8 個元素,哈希表總大小是 13, 它的 load factor 就是 8/13≈0.628/13≈0.62。當我們繼續往哈希表插入數據的時候,很快就不夠用了。 通常當負載因子開始超過 0.8 的時候,就要新開辟空間并且重新進行散列了。

重哈希(Rehashing)

當負載因子超過 0.8 的時候,需要進行 rehashing 操作了。步驟就是重新開辟一塊新的空間,開多大呢?感興趣的話可以看下 cpython 的 dictobject.c 文件然后搜索 GROWTH_RATE 這個關鍵字,你會發現不同版本的 cpython 使用了不同的策略。python3.3 的策略是擴大為已經使用的槽數目的兩倍。開辟了新空間以后,會把原來哈希表里 不為空槽的數據重新插入到新的哈希表里,插入方式和之前一樣。這就是 rehashing 操作。

HashTable ADT

實踐是檢驗真理的唯一標準,這里我們來實現一個簡化版的哈希表 ADT,主要是為了讓你更好地了解它的工作原理,有了它,后邊實現起 dict 和 set 來就小菜一碟了。 這里我們使用到了定長數組,還記得我們在數組和列表章節里實現的 Array 吧,這里要用上了。

解決沖突我們使用二次探查法,模擬 cpython 二次探查函數的實現。我們來實現三個哈希表最常用的基本操作,這實際上也是使用字典的時候最常用的操作。

  • add(key, value)
  • get(key, default)
  • remove(key)

?
具體的實現和代碼編寫在視頻里講解。這個代碼可不太好實現,稍不留神就會有錯,我們還是通過編寫單元測試驗證代碼的正確性。公眾號:學習py最風sao的方式歡迎大家繼續關注!

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的哈希表数据结构_算法与数据结构-哈希表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。