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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

哈希(Hash) - 开散列/闭散列

發布時間:2024/1/18 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 哈希(Hash) - 开散列/闭散列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄:

  • 認識哈希
  • 哈希函數
  • 處理沖突的方法
    • 閉散列(開放定址法)
    • 開散列(鏈地址法)
  • 哈希表閉散列實現
    • 閉散列基本框架
    • 哈希表閉散列插入(insert)
    • 哈希表閉散列刪除(erase)
    • 哈希表閉散列查找(find)
  • 哈希表開散列實現
    • 開散列基本框架
    • 哈希表開散列插入(insert)
    • 哈希表開散列刪除(erase)
    • 哈希表開散列查找(find)
  • 哈希相關問題思考

認識哈希

🛸順序結構以及平衡樹中,元素關鍵碼與其存儲位置之間沒有對應的關系,因此查找一個元素時,需要用關鍵碼進行多次的比較。順序查找時間復雜度為O(N),平衡樹中查找元素的時間復制度為O(logN),即取決于樹的高度。而理想的搜索方法是可以不經過任何比較,在時間復雜度為O(1)的情況下從表中找到需要的元素。如果構造一種存儲結構,該結構可通過某種函數使元素的存儲位置與它的關鍵碼之間能夠建立一一映射的關系,那么查找元素時就可以直接定位元素的位置,使查找元素的效率大大提高。

?該結構是如何存儲和查找元素的呢?
插入元素: 根據待插入元素的關鍵碼計算出該元素在此存儲結構中對應的位置進行插入。
查找元素: 對元素的關鍵碼進行同樣的計算,計算出的函數值作為元素的存儲位置,在結構中按此位置進行元素的比較,若關鍵碼相等,則查找成功。

這種存儲元素的方式即為哈希(散列)方法,哈希方法中使用的關鍵碼轉換函數稱為哈希(散列)函數,構造出來的結構稱為哈希表(Hash Table),也可以叫做散列表。

🌰舉個例子:將以下數據插入哈希表中 {1,5,6,8,9,10},哈希函數設置為 hash(key) = key % capacity ,capacity 為存儲元素底層空間的總大小。
其各元素對應的位置如下圖所示:

當我們需要搜索某個元素時,只需要將它的關鍵碼按照對應哈希函數計算其存放位置并判斷是否待查找元素即可。但是也出現了一些問題:若多個待插入元素用哈希函數計算出來的值相等,那么就存在沖突問題了,那么應該怎么進行解決呢?

哈希沖突:
不同元素的關鍵碼通過哈希函數計算出相同的哈希地址,這稱為哈希沖突(哈希碰撞)。
在實際運用中,沖突只能盡量減小,而不能完全避免。

哈希函數

哈希函數(Hash Function),又叫做散列函數、散列算法。哈希函數能夠將任意長度的輸入值轉換成固定長度的值輸出,該值稱為散列值,輸出值通常為字母與數字組合。

引起哈希沖突的一個可能原因是:哈希函數的設計不夠合理

哈希函數的設計原則:

  • 哈希函數應該比較簡單
  • 哈希函數計算出來的地址應能夠均勻的分布在所給的空間
  • 哈希函數的定義域必須包括需要存儲的全部關鍵碼,若散列表允許有 n 個地址時,哈希函數計算出的值應該在 0 ~ n-1 之間。

以下是常見的哈希函數:

下列哈希函數中第一種和第二種較為常用,其它的可作為了解。哈希函數的設計方法有很多,哈希函數設計的越精妙,產生的哈希沖突會降低,但是哈希沖突是無法完全避免的。因此,我們只能設計更精妙的方法去降低哈希沖突的概率。

1、直接定址法

取關鍵字的某個線性函數為散列地址:Hash(key) = A*key + B
優點:簡單,且計算出的值在空間中分布比較均勻。
缺點:需要預先知道關鍵字的分布情況,使用場景較局限,通常要求數據是整數且范圍集中。

使用場景:適用于整數且數據范圍比較集中的情況。


2、除留余數法
若散列表中允許的地址數為 n ,取一個不大于 n ,且最接近或者等于 n 的質數 p 作為除數,按照哈希函數:Hash(key) = key % p (p<=n),將關鍵碼轉換為哈希地址。
優點:簡單且使用場景較廣泛。
缺點:存在一定的哈希沖突,若哈希沖突較多而無法解決,將會導致效率大大的下降。


3、平方取中法
將數據中的關鍵字進行平方運算,抽取運算結果中間部分的值作為哈希地址。如:若關鍵字為1234 ,1234^2 = 1522756,因此可取中間的三位數 227 用來作為哈希地址。

使用場景:關鍵碼位數不是很大,不知道關鍵碼的分布情況。


4、折疊法
折疊法是將關鍵字分割成位數相等的幾部分(最后一部分可以短一些),然后將這幾部分疊加求和,并按照散列表表長,取后幾位作為哈希地址。

使用場景:預先不知道關鍵字的分布,適合關鍵字位數比較多的情況。


5、隨機數法
選擇一個隨機函數,取關鍵字的隨機函數值作為哈希地址,即 Hash(key) = random(key),其中 random 為隨機數函數。

使用場景:適用于關鍵字長度不等


6、數學分析法
設有 n 個 d 位數,每一個可能有 r 種不同的符號,這 r 種不同的符號在各位上出現的頻率不一定相同,可能在某些位上分布比較均勻,每種符號出現的概率均等,在某些位上分布不均勻只有某幾種符號經常出現。可以根據散列表的大小,選擇其中各種符號分布均勻的若干位作為哈希地址。

使用場景:關鍵字位數比較大的情況,事先知道關鍵字的分布且關鍵字的若干位分布較均勻的情況。

處理沖突的方法

在散列函數的構造過程中,我們盡量使散列地址均勻地分布與整個地址空間,但在實際應用中,沖突只能盡量減小,而不能完全避免。接下來我們要解決的是在哈希沖突發生時,如何有效的解決它。常用的處理沖突的方法有兩種:閉散列(開放地址法 - Open Addressing)和 閉散列(鏈地址法 - Linear Probing)。

閉散列(開放定址法)

閉散列:也叫做開放定址法,當發生哈希沖突時,若哈希表未被填滿,說明哈希表中還有空的存儲位置,這時就需要將沖突的這個值存在另外一個空位置去,那我們應該怎樣尋找那個空位置呢?我們較為常用的有以下兩種方法:

1、線性探測

線性探測即從發生沖突的位置開始,依次向后探測,直到尋找到下一個空位置即可。它的基本公式是:Hash(key) = (key + d) mod TableSize (d = 1,2,3,4......) .

例如:將序列{10,3,5,7,0,13,17}用除留余數法插入到表長為10的哈希表中。

  • 通過哈希函數獲取待插入元素在哈希表中的位置
  • 若該位置中沒有元素則直接插入新元素,若該位置中已經有元素發生哈希沖突,則使用線性探測找到下一個空位置,插入新的元素。

序列插入過程如下:

通過以上插入數據的演示我們可以發現,隨著插入數據的增多, 插入的數據產生沖突的概率也增加了。哈希表中的元素越多,查找數據時的效率就越低。因此,引入了負載因子(載荷因子)的概念。

載荷因子 = 表中元素的個數 / 散列表的空間大小
載荷因子越大,產生沖突的可能性就越大;
載荷因子越小,產生沖突的可能性就越小;

?哈希表什么情況下進行擴容?怎樣擴容?

負載因子越大,哈希沖突的概率越高,效率降低。負載因子越小,哈希表沖突的概率越小,但也意味著哈希表中的空間利用率越低,此時很多空間都被浪費了。對于開放定址法,載荷因子是特別重要的因素,一般控制在 0.7 - 0.8 以下,若超過 0.8 則會導致在表中查詢數據時的 CPU 緩存不命中(cache missing) ,按照質數曲線上升。因此,一些采用開放定址法的Hash庫,如Java的系統限制了負載因子為0.75,若超過此值則采用擴容的機制。

總結:線性探測的實現原理非常簡單且易于理解,但是卻存在著一些問題,若產生的沖突較多,所有沖突連成一片,則產生數據堆積,繼續插入數據時發生踩踏效應(需要多次比較尋找位置),因此導致哈希表的搜索效率降低。

2、二次探測
線性探測的缺陷是產生沖突的數據堆積在一起 ,這與其找下一個空位置的方法有關,因為找空位置的方式是挨著逐個往后查找,因此二次探測為了避免這個問題,找下一個空位置的公式為::Hash(key) = (key + d^2) mod TableSize (d = 1,2,3,4......) .

例如:將序列{1,3,11,12,5,17,7}用除留余數法插入到表長為10的哈希表中。當發生哈希沖突時使用二次探測的方法尋找下一個空位置。

序列插入過程如下:

研究表明:當表的長度為質數且表的負載因子不超過0.5時,新的表項一定能夠插入,而且任意一個位置都不會被探測兩次。因此只要表中有一半的空位置,就不會存在表滿的問題。在搜索時可以不考慮表滿的情況,但是在插入時一般情況下確保負載因子不超多0.7,超出則需要考慮擴容了。

散列表最大的缺陷就是空間利用率較低,這也是哈希的缺陷。

開散列(鏈地址法)

開散列又叫鏈地址法,首先對關鍵碼集合用散列函數計算散列地址,具有相同地址的關鍵碼歸于同一子集合,每一個子集稱為一個桶,各個桶中的元素通過一個單鏈表鏈接起來,桶中存儲各鏈表的頭節點。

例如:用除留余數法將序列{47,7,29,11,16,92,22,8,3,50,37,89,94,21}插入到表長為11的哈希表中,散列函數為:Hash(key) = key % 11。用分離鏈接法處理沖突,如下如所示:

一般情況下,每個結點對應的鏈表結點都很少,將n個關鍵碼通過某個散列函數,存放在散列表中的m個桶中,平均每一個桶中存儲n/m個數據。以搜索平均長度為n/m的鏈表代替了搜索長度為n的順序表,搜索效率增加了。

🧩用鏈地址法處理哈希沖突,需要增加鏈表指針,增加了存儲開銷。事實上,由于開放地址法需要確保搜索效率而浪費了大量的空間,而表項所占的空間又比指針大得多,因此使用鏈地址法相較于開放地址法更加節省空間。開散列的哈希桶,負載因子可以超過1,一般情況下控制在[0.0,1.0]之間

?桶的數量是一定的,隨著元素的不斷插入,每個桶中元素的個數不斷增多,極端情況下,可能會導致一個桶中鏈表很長,影響哈希表的性能。因此在一定條件下需要對哈希表進行增容。此時哈希表中的數據會全部重新插入到增容后的哈希表中,此時降低了沖突的概率。

在新版的Java中,為了避免數據沖突太多導致效率低下的情況,當某個哈希桶中的數據超過8個時,就將該桶下的單鏈表結構改為紅黑樹結構。而當桶中的數據減小到8個及以下時,又將紅黑樹結構退回為單鏈表結構。因此就算沖突的數據很多也解決了效率的問題。

如下圖所示:

哈希表閉散列實現

閉散列基本框架

在閉散列實現的哈希表中,哈希表的每個存儲位除了存儲數據之外還應該存儲一個狀態值來標識該位置的狀態,其中可用3個狀態位(EMPTY(沒有存儲數據),EXITS(當前位置已存儲數據),DELTE(當前位置存在的數據被刪除))來標識每個存儲位當前的存儲情況。

// 哈希表每個空間給個標記 // EMPTY此位置為空, EXITS此位置已有元素, DELETE元素已經刪除 enum State{EMPTY,EXITS,DELETE};

?為什么每個存儲位都需要標識自身的狀態呢?
例如:我們需要查找數據25,但是在查找數據之前對數據15進行了刪除,因此我們在算出25的哈希位置是5之后開始進行對比依次向后進行查找,若找到數據25則判斷為存在,若遇到空格還沒有找到就停下來,說明哈希表中沒有存儲此數據。在此案例中,15被刪除了,所以查找到下標為6的位置為空則停止查找,就找不到25了。因此我們需要用一個狀態去標識每個存儲位的實際情況。

顯然,我們不可能用0,1這樣的數字標識符去標識狀態,因為哈希表中可能存儲這樣的數據。因此,我們用三個狀態(EMPTY,EXITS,DELETE)來標識。當我們在哈希表中查找數據時,若當前位置數據與待查找數據不匹配,且當前位置的狀態是EXITS或者DELETE時,都應該繼續往后進行查找,若遇到狀態為EMPTY則不存在待查找的數據。插入元素時,可將元素插入到狀態為EMPTY和DELETE的位置,并將狀態改為EXITS。

// 閉散列 namespace CloseHash {// 標識存儲位置狀態enum State{EMPTY, EXITS,DELETE,};template<class K,class V>struct HashDate{pair<K, V> _kv; // 存儲數據的鍵值對State _state; // 標記狀態};template<class K,class V,class HashFunc>class HashTable{public:private:vector<HashDate<K, V>> _table; // vector作為底層數據結構// 為了插入元素時計算當前哈希表的負載因此,因此我們增加此變量size_t _n = 0; // 存儲的有效數據個數}; }

哈希表閉散列插入(insert)

哈希表中插入數據的步驟如下:

  • 查找哈希表中是否存在此鍵值對,若存在則不進行插入。
  • 判斷是否需要對哈希表的大小進行調整,若第一次插入哈希表大小為0或者負載因子不符合要求都需要調整哈希表大小。
  • 檢測待插入元素算出的哈希地址對應的位置中是否已存在元素,不存在則直接插入,若產生哈希沖突則從該位置開始,采用線性探測(二次探測)依次向后尋找一個狀態為EMPTY或DELETE的位置,將該元素插入,并設置該位置的狀態為EXITS。
  • 更新哈希表中有效元素個數。

哈希表的初始空間大小為0時,直接將其初始化為10(根據具體情況設置初始值),負載因子的大小控制在0.7及以下,若負載因子大于0.7則創建一個空間大小為原始哈希表空間大小2倍的新哈希表。然后遍歷原始哈希表,將其中數據插入新的哈希表中,然后交換兩個哈希表即可。

?注意: 將原始哈希表中的數據插入新的哈希表中時需要根據新的哈希表空間大小計算對應的哈希位置,而不是直接將原始哈希表中的數據挪到新哈希表對應的位置中去。

插入代碼如下:

bool insert(const pair<K, V>& kv) {// 檢測待插入的數據在哈希表中是否已經存在if (find(kv.first)){return false; // 若待插入值已存在,則插入失敗}// 檢測負載因子大小,負載因子到0.7以上就需要擴容if (_table.size() == 0 || _n * 10 / _table.size() > 7){size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;// 擴容以后,需要重新映射位置到新哈希表中// 方法一://vector<HashDate> newTable;//newTable.resize(newSize * 2);//for (auto& e : _table)//{// // 重新計算哈希地址將元素插入新表中,與下面邏輯類似//}//_table.swap(newTable);// 方法二:// 用哈希表定義一個對象HashTable<K, V, HashFunc> newHT;newHT._table.resize(newSize);for (auto& e : _table){if (e._state == EXITS){newHT.insert(e._kv);}}// 與對象中的哈希表交換_table.swap(newHT._table);}// 用哈希函數算出哈希表中的映射位置HashFunc hf;size_t start = hf(kv.first) % _table.size();size_t index = start;// 探測待插入元素位置 - 線性探測 or 二次探測size_t i = 0;while (_table[index]._state == EXITS){//index = start + i * i; // 二次探測index = start + i; // 線性探測index %= _table.size(); // 防止下標越界++i;}// 將待插入元素插入對應位置,并將其狀態改為EXITS,將哈希表中有效元素個數+1_table[index]._kv = kv;_table[index]._state = EXITS;++_n;return true; }

哈希表閉散列刪除(erase)

因為標識了每個存儲位置的狀態,因此刪除元素的時候直接采用偽刪除的方式即可,也就是將要刪除位置的狀態改為DELETE。偽刪除也不會影響新的元素插入,因為插入新的元素只需要將該位置元素覆蓋即可。

哈希表閉散列刪除元素的步驟:

  • 檢測待刪除的元素在哈希表中是否存在,若不存在則刪除失敗。
  • 若存在,則將該位置的狀態更改為DELETE。
  • 更新哈希表中的有效元素個數。

刪除代碼如下:

bool erase(const K& key) {// 查找該元素是否在哈希表中存在HashDate<K, V>* ret = find(key);if (ret == nullptr)return false;else // 找到該元素{// 將其狀態置為DELETE,并將有效元素個數-1ret->_state = DELETE;--_n;return true;} }

哈希表閉散列查找(find)

哈希表閉散列查找元素的步驟:

  • 判斷哈希表大小是否為0,若是,則查找失敗。
  • 通過插入元素的哈希函數計算待查找元素的哈希地址。
  • 從計算出的位置開始,按照哈希沖突的解決方法(線性探測 or 二次探測)依次向后進行元素查找,直到查找到該元素,或者是找到一個狀態為EMPTY的位置則查找失敗,表明該哈希表中沒有此元素。

查找代碼如下:

HashDate<K, V>* find(const K& key) {// 表的大小為0,則查找失敗,返回nullptrif (_table.size() == 0){return nullptr;}// 計算待查找元素的哈希地址HashFunc hf;size_t start = hf(key) % _table.size();size_t index = start;size_t i = 1; // 初始值設定為1,映射位置查找不到,則直接到下一個位置查找// 哈希表中存儲位置狀態不為空,則繼續查找while (_table[index]._state != EMPTY){// 找到待查找元素,并返回它的地址if (_table[index]._state == EXITS && _table[index]._kv.first == key){return &_table[index];}// 該位置不是待查找元素,繼續按照相應規則向后查找//index = start + i * i; // 二次探測index = start + i; // 線性探測index %= _table.size();++i;}// 走到存儲位置為空的狀態,不存在此元素,返回空指針return nullptr; }

哈希表開散列實現

開散列基本框架

開散列的哈希表中,哈希表的每個位置存儲的是一個單鏈表的頭節點,即每個哈希桶存儲的數據是一個結點類型,該節點中包含數據域和指針域。開散列的實現也需要判斷負載因子的大小,因此需要記錄當前開散列中有效元素的個數。

namespace Bucket {template<class K,class V>struct HashNode{HashNode<K, V>* _next; pair<K, V> _kv;HashNode(const pair<K, V>& kv):_next(nullptr),_kv(kv){}};template<class K,class V,class HashFunc>class HashTable{// 重定義一下,方便使用typedef HashNode<K, V> Node;public:private:vector<Node*> _table; // 表的每個元素為一個結點指針size_t _n = 0; // 有效數據個數}; }

哈希表開散列插入(insert)

開散列中插入數據步驟:

  • 檢測哈希表中是否已存在待插入的數據,若存在則插入失敗。
  • 根據負載因子來判斷是否需要擴容(一般負載因子>=1則擴容)。
  • 按照對應哈希函數計算待插入元素哈希地址頭插到哈希表中,表中有效元素個數+1。

注意:在擴容的時候,需要將原哈希表的數據插入到新哈希表中,這里可以通過復用插入函數進行數據的重新插入,但在這個過程中需要創建相同數據的結點插入到新哈希表中,還需要清理原哈希表。可以這樣做,但是沒有必要。事實上,我們只需要遍歷原哈希表的每個哈希地址,通過哈希函數計算每個數據在新哈希表中的對應位置,只需要將該節點取下來接在新的哈希表中即可。省略了創建節點和釋放節點的過程。

插入代碼如下:

bool insert(const pair<K, V>& kv) {// 檢測該值是否存在哈希表中,存在則返回falseif (find(kv.first))return false;HashFunc hf;// 負載因子到1時進行擴容if (_table.size() == _n){// 不推薦這樣寫/*size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;HashTable<K, V, HashFunc> newHT;newHT._table.resize(newSize,nullptr);for (size_t i = 0;i < _table.size();++i){Node* cur = _table[i];while (cur){newHT.insert(cur->_kv); cur = cur->_next;}}newHT._table.swap(_table);*/size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;vector<Node*> newTable;newTable.resize(newSize, nullptr);// 遍歷取原表中節點,重新算映射到新表中的位置,接到新表中for (size_t i = 0;i < _table.size();++i){Node* cur = _table[i];// 遍歷當前哈希地址中的數據while (cur){// 保存當前需要摘取節點的下一個節點Node* next = cur->_next;// 計算當前節點在新表中對應的哈希地址并頭插到新的哈希表中size_t index = hf(cur->_kv.first) % newSize;cur->_next = newTable[index];newTable[index] = cur;cur = next;}_table[i] = nullptr;}newTable.swap(_table);}// 計算待插入節點的哈希地址,將待插入節點頭插到對應哈希地址下size_t index = hf(kv.first) % _table.size();Node* newnode = new Node(kv);newnode->_next = _table[index];_table[index] = newnode;// 更新有效元素個數++_n;return true; }

哈希表開散列刪除(erase)

開散列中刪除數據步驟:

  • 檢測哈希表大小是否為0,若是則返回false。
  • 通過對應哈希函數計算出該元素對應的哈希地址,遍歷對應的哈希桶,查找待刪除結點。
  • 若找到了對應的節點,則將該節點刪除,并更新有效元素個數。

刪除代碼如下:

bool erase(const K& key) {// 檢測哈希表大小是否為0if (_table.size() == 0)return false;// 計算待刪除元素對應的哈希地址HashFunc hf;size_t index = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[index];while (cur){// 找到待刪除元素if (cur->_kv.first == key){// 判斷待刪除元素是第一個節點還是中間節點if (_table[index] == cur)_table[index] = cur->_next;elseprev->_next = cur->_next;--_n;delete cur;return true;}// 向后查找prev = cur;cur = cur->_next;}// 沒有找到待刪除元素return false; }

哈希表開散列查找(find)

開散列中查找數據步驟:

  • 檢測哈希表的大小是否為0,如為0則查找失敗。
  • 按照哈希函數計算待查找數據的哈希地址。
  • 通過哈希地址找到對應的哈希桶,然后遍歷單鏈表進行查找。

查找代碼如下:

Node* find(const K& key) {if (_table.size() == 0)return nullptr;HashFunc hf;size_t index = hf(key) % _table.size();Node* cur = _table[index];while (cur){if (cur->_kv.first == key)return cur;cur = cur->_next;}return nullptr; }

哈希相關問題思考

?哈希函數計算哈希地址時只能用整數去計算,其它類型應該怎么解決呢?

哈希函數采用除留余數法,被模的key必須要為整數才可以處理,此處提供將key轉化為整形的方法去解決此問題。具體應該怎么轉化可以看看這篇文章:各種字符串哈希函數

例如:

// 特化 template<> struct Hash<string> {// 字符串轉成對應的一個整形值,因為整形才能取模算映射位置// 盡量讓字符串不同,轉出的整形值盡量不同size_t operator()(const string& s){// "abc" "cba"size_t value = 0;for (auto ch : s){value += ch;// BKDL Hashvalue *= 131;}return value;} };

?哈希表的大小為什么建議是素數?

對于哈希表的大小最好為素數這個問題目前是存在爭議的。若對其原因感興趣可以去網上找來看看,這里就不解釋了,因為也解釋不準確。

那么我們應該如何快速獲取一個類似兩倍關系的素數呢?哈希增容兩倍時哈希表的大小就不再是素數了,找到下一個素數也非常不容易,因此我們可以提前準備一個素數表,需要的時候直接從中獲取即可。如下所示:以下表中的素數都以接近2倍的速度增長。

size_t GetNextPrime(size_t prime) {const int PRIMECOUNT = 28;static const size_t primeList[PRIMECOUNT] ={53ul, 97ul, 193ul, 389ul, 769ul,1543ul, 3079ul, 6151ul, 12289ul, 24593ul,49157ul, 98317ul, 196613ul, 393241ul, 786433ul,1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,1610612741ul, 3221225473ul, 4294967291ul};size_t i = 0;for (; i < PRIMECOUNT; ++i){if (primeList[i] > prime)return primeList[i];}return primeList[i]; }

增容的時候,直接獲取下一個素數即可。

newTable.resize(GetNextPrime(_table.size()));

總結

以上是生活随笔為你收集整理的哈希(Hash) - 开散列/闭散列的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 永久免费无码av网站在线观看 | 婷婷中文 | 男人天堂网在线 | 蜜臀av一区二区三区激情综合 | 风韵少妇性饥渴推油按摩视频 | 欧美日韩国产一区二区三区在线观看 | av福利站| 亚洲无码久久久久久久 | av黄色片在线观看 | 国精产品一二三区精华液 | 免费观看国产精品视频 | 在线免费看污片 | 国产成人精品一区二区三区网站观看 | 蜜臀aⅴ国产精品久久久国产老师 | 国产页| 武林美妇肉伦娇喘呻吟 | 久久这里只有精品8 | 日本一区二区不卡在线观看 | 五月婷婷av | 日韩无遮挡 | 欧美视频在线观看一区二区 | 波多野吉衣在线视频 | 日批免费观看视频 | 天天躁日日躁狠狠躁喷水 | 精品一区二区三区免费毛片爱 | 一区二区欧美日韩 | 九九久久99| 亚洲精品美女网站 | av嫩草 | 久久久96人妻无码精品 | 日本欧美激情 | 99精品影视| 欧美色综合网站 | 亚洲男女啪啪 | 久久久久久久综合 | 日本午夜视频在线观看 | 黄色片aa| 日韩黄色精品 | 成人免费视频播放 | 午夜精彩视频 | 日韩福利 | 国产十区| 国产精品无码一区二区三区免费 | 一本色道久久88综合日韩精品 | 97潮色 | 亚洲永久免费视频 | 国产精品av一区 | 国产av日韩一区二区三区精品 | 国产视频久久久久久久 | 大奶子在线观看 | 黄色wwwww| 久久国产成人精品国产成人亚洲 | 欧美无吗 | 亚洲欧美校园春色 | 台湾佬中文在线 | 国产精品久久久久久久久 | 天堂av2021 | 欧美一级视频 | 中文字幕9 | 影音先锋在线中文字幕 | 日韩三级av | 国产猛男猛女超爽免费视频 | 欧美中文字幕在线观看 | 色噜噜在线播放 | 一区二区三区四区在线观看视频 | 97精品国产97久久久久久粉红 | 男女扒开双腿猛进入爽爽免费 | 在线观看日本一区 | 秋霞av一区二区三区 | 丁香九月激情 | 中文字幕免费在线看线人动作大片 | 亚洲网站免费 | 亚洲区 欧美区 | 少妇裸体淫交视频免费看高清 | 久久大陆 | 俄罗斯美女一级爱片 | 亚洲69视频 | 久操欧美 | 欧美二区在线 | 国产高清无遮挡 | 又大又粗又爽18禁免费看 | 一级日批片 | 影音先锋在线看片资源 | 亚洲好骚综合 | 福利视频网站 | 美腿丝袜亚洲综合 | 午夜精品久久久久久久久 | 懂色av,蜜臀av粉嫩av | 蜜臀视频一区二区 | 午夜三级影院 | 嫩草网站在线观看 | 丝袜美腿中文字幕 | 97久久国产 | 亚洲欧美日本在线观看 | 水蜜桃av无码 | 538精品在线视频 | 天天曰夜夜操 | 亚洲精品影片 | 少妇色 |