哈希表特性與unordered_map/unordered_set實現分析

目錄

一、哈希表核心特性總結

1.開放地址法

2.鏈地址法

二、unordered_map/unordered_set實現要點分析

1. 哈希表核心實現(HashTable2.h)

(1) 哈希函數處理

(2) 鏈地址法實現

(3) 迭代器設計

(4) hashtable設計

2. unordered_map實現要點

3. unordered_map實現要點


一、哈希表核心特性總結

哈希表有兩種表 :?一種是閉散列(開放地址法),一種是開散列(鏈地址法),我將用畫圖來帶大家理解這兩種方法的思路

1.開放地址法

線性探測

v

2.鏈地址法

二、unordered_map/unordered_set實現要點分析

1. 哈希表核心實現(HashTable2.h)

(1) 哈希函數處理

僅使用首字符會導致大量沖突(如所有以相同字母開頭的字符串),使用BKDR哈希,通過累乘質數和字符值獲得更好分布

// 默認哈希函數(直接類型轉換)
template<class K>
struct DefaultHashFunc {size_t operator()(const K& key) {return (size_t)key;}
};// 字符串特化版本
template<>
struct DefaultHashFunc<string> {size_t operator()(const string& str) {size_t hash = 0;for(auto ch : str) {hash += 131;hash += ch;}return hash;}
};
(2) 鏈地址法實現
template<class T>
struct HashNode
{T _data;HashNode<T>* next;HashNode(const T& data):_data(data), next(nullptr){}
};
(3) 迭代器設計
//前置申明,告訴Iterator,申明了哈希表
template<class K, class T, class KeyOfT, class HashFunc >
class HashTable;template<class K, class T,class Ptr,class Ref, class KeyOfT, class HashFunc>
struct HTIterator
{typedef HashNode<T> Node; typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc>  Self;//這是什么鬼??typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc>  Iterator;Node* _node;//就是不能改*phtconst HashTable<K, T, KeyOfT, HashFunc>* _pht;//為什么需要節點的指針和哈希的指針/*HTIterator(Node * node,HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}*///這個_pht加了const的重載HTIterator(Node* node,const  HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node), _pht(pht){}//普通迭代器時,它是拷貝構造//const迭代器時,它是構造HTIterator(const Iterator & it):_node(it._node), _pht(it._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->next){//當前桶還沒完_node = _node->next;}else{KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();//從下一個位置,查找不為空的桶++hashi;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){//不為空就退出_node = _pht->_table[hashi];return (*this);}else{//為空繼續加 ++hashi;}}_node = nullptr;}return *this;}bool operator!=(const Self& s){return  _node != s._node;}bool operator==(const Self& s){return  _node == s._node;}
};
(4) hashtable設計
	template<class K, class T,class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;////友元聲明,類模版需要把模版參數帶上template<class K, class T,class Ptr,class Ref ,class KeyOfT, class HashFunc >friend struct HTIterator;		public:typedef HTIterator<K, T,T*,T&, KeyOfT, HashFunc>  iterator;typedef HTIterator<K, T,const T*,const  T&, KeyOfT, HashFunc>  const_iterator;iterator begin(){//找第一個桶for (size_t i =0 ;i < _table.size();i++){Node* cur = _table[i];if (cur){//這里為什么傳this ???  航哥說這里this是哈希表的指針return iterator(cur, this);}}//沒有找到return iterator(nullptr, this);}iterator end(){return iterator(nullptr,this);}const_iterator begin()const{//找第一個桶for (size_t i = 0;i < _table.size();i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this);}}//沒有找到return const_iterator(nullptr, this);}const_iterator end()const{return const_iterator(nullptr, this);}HashTable(){//先把size開到10,然后把剩余的位置另存為空指針_table.resize(10, nullptr);}~HashTable(){for (size_t i = 0;i < _table.size();i++){Node* cur = _table[i];while (cur){Node* next = cur->next;delete cur;//	free cur;cur = next;}//因為cur是野指針,如果不置空,那么他有可能還會指向原來的節點cur = nullptr;}}pair<iterator,bool> insert(const T& data){KeyOfT kot;HashFunc hf;iterator it = Find(kot(data));//在這里是證明有相同的內容if (it!=end()){return make_pair(it,false);}//負載因子到一就擴容if (_n == _table.size()){size_t newSize = _table.size() * 2;//創建新表HashTable<K,T,KeyOfT,HashFunc> newht;//這個需要開新節點,而且銷毀也麻煩//for (size_t i = 0;i < _table.size();i++)//{//	//.......//	ht.insert();//}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 hashi = hf(kot(cur->_data)) % newSize;cur->next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();//頭插,這個沒看懂Node* newnode = new Node(data);newnode->next = _table[hashi];_table[hashi] = newnode;++_n;return make_pair(iterator(newnode,this), true);}iterator Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur,this);}cur = cur->next;}return iterator(nullptr,this);}void Print(){for (size_t i = 0;i < _table.size();i++){printf("[%d]->", i);Node* cur = _table[i];while (cur){cout << cur->_kv.first << "->" << cur->_kv.second << "->";cur = cur->next;}printf("NULL\n");}}bool Erase(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){if (prev == nullptr){_table[hashi] = cur->next;}else{prev->next = cur->next;}delete cur;return true;}prev = cur;cur = cur->next;}--_n;return false;}private:vector<Node*> _table;//指針數組size_t _n = 0;};

2. unordered_map實現要點

template<class K,class V>
class  unordered_map
{struct MapKeyOfT{const K& operator()(const pair<K,V>& kv){return kv.first;}};
public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator  iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator  const_iterator;pair<iterator,bool> insert(const pair<K,V>& kv){return _ht.insert(kv);}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}V& operator[](const K& key){pair<iterator,bool> ret=_ht.insert(make_pair(key,V()));return ret.first->second;}
private://這種const的特殊屬性,一般是自己設置hash_bucket::HashTable<K,pair<const K,V>,MapKeyOfT >  _ht;
};

3. unordered_map實現要點

template<class K>
class unordered_set
{struct SetKeyOfT{const K & operator()(const K & key){return key;}};public:typedef	typename hash_bucket::HashTable<K,K,SetKeyOfT>::const_iterator iterator;typedef	typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin()const
{return _ht.begin();
}iterator end()const
{return _ht.end();
}
pair<iterator,bool> insert(const K& key)
{//這樣寫是錯的,因為這里接受的是const_iterator,返回的是iteratorpair<hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.insert(key);return make_pair(ret.first, ret.second);
}private:hash_bucket::HashTable<K, K,SetKeyOfT>  _ht;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918773.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918773.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918773.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

生產環境sudo配置詳細指南

目錄 1. 語法格式 2. 配置示例 3. 使用 /etc/sudoers.d/ 目錄管理&#xff08;推薦&#xff09; 4. 基礎配置&#xff1a;用戶權限管理 4.1 ??添加用戶到sudo組 ??4.2 驗證用戶組信息 5. sudo日志配置 5.1 修改sudoers配置文件 5.2 創建日志目錄與權限設置 6. Su…

CSS動態視口單位:徹底解決移動端適配頑疾,告別布局跳動

你是否曾被這些問題困擾&#xff1a; 移動端頁面滾動時&#xff0c;地址欄收縮導致頁面高度突變&#xff0c;元素錯位&#xff1f;100vh在移動設備上實際高度超出可視區域&#xff1f;全屏彈窗底部總被瀏覽器UI遮擋&#xff1f; 這些痛點背后都是傳統視口單位的局限——無法響應…

【P27 4-8】OpenCV Python——Mat類、深拷貝(clone、copyTo、copy)、淺拷貝,原理講解與示例代碼

P27 4-8 1 Mat結構體2 深拷貝VS淺拷貝3 代碼示例1 Mat結構體 2 深拷貝VS淺拷貝 只拷貝了頭部&#xff0c;header&#xff0c;&#xff0c;但是data部分是共用的&#xff0c;速度非常快&#xff1b; 缺點&#xff0c;任意一個修改&#xff0c;另一個data跟著變&#xff0c;這就是…

容器運行時支持GPU,并使用1panel安裝ollama

前言 安裝Docker請看之前博文&#xff1a;Docker實戰中1panel方式安裝Docker。 安裝 NVIDIA 容器工具包 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html 安裝 先決條件 閱讀有關平臺支持的部分。為您的 Linux 發行版安裝…

高并發內存池 性能瓶頸分析與基數樹優化(9)

文章目錄前言一、性能瓶頸分析操作步驟及其環境配置分析性能瓶頸二、基數樹優化單層基數樹二層基數樹三層基數樹三、使用基數樹來優化代碼總結前言 到了最后一篇嘍&#xff0c;嘻嘻&#xff01; ??終于是要告一段落了&#xff0c;接下來我們將學什么呢&#xff0c;再說吧&…

C#面試題及詳細答案120道(01-10)-- 基礎語法與數據類型

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

機器翻譯:回譯與低資源優化詳解

文章目錄一、機器翻譯的瓶頸二、回譯&#xff08;Back-Translation&#xff09;2.1 什么是回譯&#xff1f;2.2 為什么回譯有效&#xff1f;2.3 回譯的缺點與挑戰三、低資源優化詳解3.1 數據層面策略3.2 模型層面策略3.3 架構層面策略四、回譯與低資源優化對比4.1 回譯與低資源…

leetcode-python-344反轉字符串

題目&#xff1a; 編寫一個函數&#xff0c;其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 s 的形式給出。 不要給另外的數組分配額外的空間&#xff0c;你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 示例 1&#xff1a; 輸入&#xff1a;s [“h”,“…

【Python】新手入門:什么是python字符編碼?python標識符?什么是pyhon保留字?

?? 個人主頁:(時光煮雨) ?? 高質量專欄:vulnhub靶機滲透測試 ?? 希望得到您的訂閱和支持~ ?? 創作高質量博文(平均質量分95+),分享更多關于網絡安全、Python領域的優質內容!(希望得到您的關注~) ??文章目錄?? 前言 ??一、編碼 ??二、標識符 ??三、Py…

為什么要使用消息隊列呢?

消息隊列&#xff08;Message Queue&#xff0c;MQ&#xff09;在分布式系統中扮演著 ?異步通信樞紐? 的角色&#xff0c;其核心價值在于解決系統間的解耦、流量削峰、異步處理等關鍵問題。以下是它的核心價值及典型應用場景&#xff1a;?? 一、核心價值&#xff1a;解決什…

ROS機器人云實踐案例博客建議和范文-AI版本

海報圖AI圖1AI圖2zhangrelay的博客以技術深度、跨界思考和社會洞察為特色&#xff0c;內容兼具實用性與前瞻性&#xff0c;但部分觀點存在爭議&#xff0c;需結合具體主題辯證看待。以下從內容特色、技術深度、社會洞察、爭議點四個維度展開分析&#xff1a;一、內容特色&#…

UE小:編輯器模式下「窗口/鼠標不在焦點」時仍保持高幀率

要在UE編輯器模式下「窗口/鼠標不在焦點」時仍保持高幀率&#xff0c;可按下面做法&#xff1a; 關閉編輯器的后臺降頻選項&#xff1a;在 Edit -> Editor Preferences -> General -> Performance 中取消勾選 “Use Less CPU when in Background”。

VS2022 + Qt 5.15.2+Occ開發環境搭建流程

Visual Studio 2022 Qt 5.15.2 圖形處理開發環境搭建流程 1. 安裝 Visual Studio 2022 下載安裝程序&#xff1a;Visual Studio 官網選擇工作負載&#xff1a; ?? “使用C的桌面開發”?? “通用Windows平臺開發”&#xff08;可選&#xff09; 安裝組件&#xff1a; ??…

多任務并發:進程管理的核心奧秘

多任務&#xff08;并發&#xff09;&#xff1a;讓系統具備同時處理多個任務的能力1. 多進程2. 多線程3. 進程間通信一、進程的基本概念1. 什么是進程&#xff1f;正在運行的程序&#xff0c;其運行過程中需要消耗內存和CPU。進程的特點&#xff1a;動態性&#xff1a;進程是程…

高效TypeScript開發:VSCode終極配置指南

?? VSCode TypeScript 專屬效率設置大全 (純 settings.json 配置) // .vscode/settings.json {/* &#x1f50d; 引用與類型追蹤 */"typescript.referencesCodeLens.enabled": true, // 顯示引用計數(點擊查看所有引用處)"typescript.implementationsCod…

資本的自我否定:四重矛盾中的歷史辯證法

資本自誕生以來&#xff0c;便以“增殖”為唯一使命&#xff0c;如同一個不知疲倦的擴張機器&#xff0c;在推動生產力飛躍的同時&#xff0c;也埋下了自我毀滅的種子。這種自我否定并非外部力量的強加&#xff0c;而是其內在邏輯的必然展開——從價格戰的困局到經濟危機的周期…

Linux系統安裝Docker及常見問題解決

1.1 解決安裝Docker問題 Linux的發行版本&#xff0c;大多數還是在用CentOS&#xff0c;雖然CentOS已經不更新了。。。。。CentOS因為不更新了&#xff0c;所以很多的yum源都失效了。導致安裝Docker失敗&#xff01; 只需要更新一下yum源。直接將之前默認的yum源替換為阿里的…

CICD-Devops整合Kubernetes-4

Devops整合Kubernetes Kubernetes部署快速安裝Kubernetes **官網&#xff1a;**https://kuboard.cn/選擇默認支持docker的版本1.19前置環境部署 所有節點均需執行同操作 # 配置主機名解析 [rootKubernetes-master ~]# echo "127.0.0.1 $(hostname)" >> /etc/ho…

C/C++ 指針與內存操作詳解——從一級指針到字符串轉換函數的完整解析

C/C 指針與內存操作詳解——從一級指針到字符串轉換函數的完整解析 本文將帶你系統理解 一級指針與二級指針的區別、數組拷貝的注意事項、字符串轉整數函數實現 等 C/C 編程中常見且易混淆的知識點&#xff0c;并配合詳細代碼示例與常見坑點分析&#xff0c;讓你從入門到掌握。…

Java -- HashSet的全面說明-Map接口的常用方法-遍歷方法

目錄 1. HashSet的全面說明 2. Map接口實現類的特點 注意&#xff1a;講的是JDK8的Map接口特點 3. Map接口的常用方法 4. Map遍歷方法 1. HashSet的全面說明 1. HashSet實現了Set接口 2. HashSet實際上是HashMap 3. 可以存放null值&#xff0c;但是只能有一個null 4. H…