使用哈希表封裝myunordered_set和myunordered_map

文章目錄

  • 使用哈希表封裝myunordered_set和myunordered_map
    • 實現出復用哈希表框架,并支持insert
    • 支持迭代器的實現
    • const
    • Key不能被修改
    • unordered_map支持[ ]
    • 結語

我們今天又見面啦,給生活加點impetus!!開啟今天的編程之路!!
在這里插入圖片描述
今天我們使用前面已經實現過的哈希表來實現myunordered_set和unordered_map
作者:?( ‘ω’ )?260
我的專欄:C++進階,C++初階,數據結構初階,題海探驪,c語言
歡迎點贊,關注!!

使用哈希表封裝myunordered_set和myunordered_map

實現出復用哈希表框架,并支持insert

與map和set相比而言,unordered系列的實現更加復雜。
首先,unoedered_set是key類型,unordered_map是key,value類型,要實現一個底層實現兩種數據結構,我們必須使用模版,這里我們傳遞三個參數,第一個是K,第二個是T,第三個是KeyOfT
三個模版的作用

第一個:傳遞K是為了find和erase接口,因為find和erase接口只能夠使用key作為實參
第二個:代表哈希表中結點的存儲類型
第三個:因為我們來尋找映射位置的時候需要取模,key一般可以直接取,但是pair必須需要取出其中的key

接下來我們來看代碼:

template<class K>
class myunordered_set
{struct SetOfT{const K& operator(const K&key){return key;}}
public:private:HashTable<K,K,SetOfT> _tables;//底層結構是哈希桶
}
template<class K,class V>
class myunordered_map
{struct MapOfT{K& operator()(const pair<K,V>&kv){return kv.first;}}
private:HashTable<K,pair<K,V>,MapOfT> _tables;底層結構是哈希桶
}

因為存儲的數值不確定,所以哈希結點也需要修改:

template<class T>
struct HashNode
{T _data;HashNode<K>*_next;HashNode(const T&data):_data(data),_next(nullptr){}
}

接下來我們來寫insert函數,insert是重點,我們主要還寫寫的是偽代碼。
Insert操作還是主題思路不變。
1:先求key映射的偏移位置(細節:key可能需要取,其次,可能需要轉換成整形)這里會用到兩層仿函數
2:不斷頭插
3:檢查是否需要擴容(細節:定義的是哈希數組,而非一個哈希表,直接把結點給他拿下來就行)
4:注意返回值:我們直接模仿庫中的insert函數返回pair<iterator,bool>類型(為什么我們實現這個insert函數重載?因為為了實現unordered_map支持operator[ ]做準備)
在這里插入圖片描述
來看代碼實現
注意:此時迭代器里面有兩個成員變量,一個是_node,一個是哈希表,原因會在下面說.

pair<iterator,bool> Insert()(const T&data)
{KeyOfT kot;//取keyHash hs;//轉整形,只有在取模的時候才用iterator it=Find(kot(data));if(it!=End()) return {it,false};//插入失敗+去重if (_n == _tables.size()){//兩種邏輯,優先第二種,第一種是拷貝結點,刪除舊結點,第二種是直接把結點給拿下來//第一種:創建新的哈希表,第二種:創建新的數組//HashTable<K, V, Hash> newht(__stl_next_prime(_tables.size() + 1));//加1才能夠繼續取到更到的素數遍歷舊表,將舊表的數據全部重新映射到新表//for (int i = 0;i < _tables.size();i++)//{//	Node* cur = _tables[i];//	while (cur)//	{//		newht.Insert(cur->_kv);//		cur = cur->_next;//	}//}//_tables.swap(newht._tables);//為什么第一種方法會重新再拷貝構造,因為復用的時候會調用new這個關鍵字vector<Node*> newtables(__stl_next_prime(_tables.size() + 1),nullptr);//數組初始化為n個nullptrfor (size_t i = 0;i < _tables.size();i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;//這里我們存儲一下cur的下一個位置,因為當我們將cur下一個位置放下來的時候,_next被修改,所以需要提前記錄一下//重新計算映射位置size_t hashi = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}//因為原來的舊鏈表的值已經被使用過了,所以我只直接將其置為nullptr_tables[i] = nullptr;}//底層是數組,例如vector或者string,調用庫中的交換函數_tables.swap(newtables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newnode = new Node(data);//這里不是new Node(kot(data));//pair類型結點里面存的是pair,不是key//優先選擇頭插進入鏈表,尾插需要向后遍歷,頭插分兩種情況,映射位置為空或有數據,下面的代碼兩者都可以適用newnode->_next = _tables[hashi];_tables[hashi] = newnode;_n++;return {Iterator(newnode,this),true};
}

支持迭代器的實現

首先,因為哈希表中成員是一個單鏈表,所以我的的迭代器肯定是單向迭代器,即我們的迭代器只能支持++,不支持- -,同樣也不支持隨機訪問。

迭代器的成員變量肯定是有一個_node。
當我們需要返回下一個位置的迭代器的時候,如果下一個位置不為空,那么迭代器中的指針直接向后走一步就可以了,如果下一個位置為空,就需要遍歷到下一個不為空的位置的桶,返回桶的第一個位置即可。
此時我的cur都在遍歷這個單鏈表了,我們根本找不到哈希表中下一個不為空的位置,那我們應該怎樣找到下一個不為空的桶呢?我們必須傳遞一個哈希表過去!!
來看代碼實現:
細節:這里我直接加上普通迭代器和const迭代器一起實現了,不然等下解釋的太繞了
實現const迭代器的話,就會多2個模版參數,主要是返回值operator*和operator->。
而且,在迭代器內部有this指針,我們只用操作_node的指向即可

template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash>
struct HTIterator
{typedef HashNode<T> Node;typedef HashTable<K,T,KeyOfT,Hash> HT;typedef HTIterator<K,T,Ref,Ptr,keyofT,Hash>  Self;Node*_node;//成員變量HT*_pht;//成員變量Self& operator++(){if(_node->_next)//指向的下一個不為空{_node=_node->_next;}else{//下一個結點為空,需要向后找到不為空的桶KeyOfT kot;Hash hs;size_t hashi=hs(kot(_node->_data))%_pht->_tables.size();hashi++;//需要跳過當前桶while(hashi<_pht->_tables.size()){if(_pht->_tables[hashi]){_node=_pht->_tables[hashi];break;}++hashi;}if(hashi == _pht->_tables.size())//哈希表遍歷遍歷完了都還沒有找到{_node=nullptr;}}return *this;}
}

接下來我再來實現迭代器中不重要的操作:
直接看代碼即可:

Ref operator*()
{return _node->_data;
}
Ptr operator->()
{return &_node->_data;//箭頭會返回對應結點的指針,編譯器為了優化,自己會再添加一個箭頭,這個箭頭就是*.操作的結合
}
bool operator==(const Self& s)
{return _node == s._node;
}
bool operator!=(const Self& s)
{return _node != s._node;
}

const

const迭代器中迭代器部分我們已經完成了,接下來我們來看HashTable的部分。
這里需要解釋Begin(),begin肯定是返回桶中鏈表的第一個結點即可

因為比較簡單,直接來看代碼即可:

Iterator Begin()//返回第一個桶的第一個結點
{if (_n == 0)return End();//此時沒有結點,Begin()就是End()for (size_t i = 0;i < _tables.size();i++){if (_tables[i])return Iterator(_tables[i] ,this);}return End();//語法邏輯上來說必須要加,不能用運行邏輯來概括語法邏輯,萬一里面的程序出錯,可能就沒有返回值了
}Iterator End()
{return Iterator(nullptr, this);
}//錯誤積累:如果報錯不能將initializer list轉換成啥,大概率編譯器是將{}識別成這個作用了,但是我的目的是多參數來進行隱式類型轉換
Const_Iterator Begin() const//返回第一個桶的第一個結點
{if (_n == 0)return End();for (size_t i = 0;i < _tables.size();i++){if (_tables[i]) return Const_Iterator(_tables[i], this);}return End();
}Const_Iterator End()const
{return Const_Iterator(nullptr, this);
}

Key不能被修改

這個也比較簡單,只要將第二個模版參數對應的K修改為const K即可,隨后對應的typedef部分也需要修改一下。

unordered_map支持[ ]

因為前面已經實現過,直接來看代碼:

V& operator[](const K& key)
{pair<iterator, bool> ret = Insert({key,V()});//直接復用上面的Insert,本質上還是調用的底層return ret.first->second;
}

結語

感謝大家閱讀我的博客,不足之處歡迎指正,感謝大家的支持
逆水行舟,楫摧而志愈堅;破繭成蝶,翼濕而心更熾!!加油!
在這里插入圖片描述

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

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

相關文章

后端框架(2):Java的反射機制

什么是java反射機制&#xff1f; 回顧之前java程序如何使用類 1.分析&#xff0c;確定類名&#xff0c;屬性名&#xff0c;方法......創建類 2.創建類的對象 3.使用 一切都是已知的。 在程序開發中&#xff0c;在哪兒需要使用哪個類的對象&#xff0c;就在那兒創建這個類對象…

ch10 課堂參考代碼

ch10 最小生成樹 生成樹&#xff1a;對于 n 個結點 m 條邊的無向圖 G&#xff0c;由全部 n 個結點和其中 n - 1 條邊構成的無向連通子圖稱為 G 的一棵生成樹。 如果圖 G 原本就不連通&#xff0c;則不存在生成樹&#xff0c;只存在生成森林。 最小生成樹&#xff08;Minimum…

費曼技巧及提高計劃

費曼技巧及提高計劃 一、什么是費曼技巧&#xff1f; 費曼技巧&#xff08;Feynman Technique&#xff09;由諾貝爾物理學獎得主理查德費曼提出&#xff0c;是一種通過“以教代學”來徹底理解復雜概念的學習方法。其核心邏輯是&#xff1a; “如果你不能簡單解釋一件事&#x…

LongRefiner:解決長文檔檢索增強生成的新思路

大語言模型與RAG的應用越來越廣泛&#xff0c;但在處理長文檔時仍面臨不少挑戰。今天我們來聊聊一個解決這類問題的新方法——LongRefiner。 背景問題&#xff1a;長文檔處理的兩大難題 使用檢索增強型生成&#xff08;RAG&#xff09;系統處理長文檔時&#xff0c;主要有兩個…

5月16日復盤-目標檢測開端

5月16日復盤 一、圖像處理之目標檢測 1. 目標檢測認知 ? Object Detection&#xff0c;是指在給定的圖像或視頻中檢測出目標物體在圖像中的位置和大小,并進行分類或識別等相關任務。 ? 目標檢測將目標的分割和識別合二為一。 ? What、Where 2. 使用場景 目標檢測用于…

MySQL基礎面試通關秘籍(附高頻考點解析)

文章目錄 一、事務篇&#xff08;必考重點&#xff09;1.1 事務四大特性&#xff08;ACID&#xff09;1.2 事務實戰技巧 二、索引優化大法2.1 索引類型全家福2.2 EXPLAIN命令實戰 三、存儲引擎選型指南3.1 InnoDB vs MyISAM 終極對決 四、SQL優化實戰手冊4.1 慢查詢七宗罪4.2 分…

Word圖片格式調整與轉換工具

軟件介紹 本文介紹的這款工具主要用于輔助Word文檔處理。 圖片排版功能 經常和Word打交道的人或許都有這樣的困擾&#xff1a;插入的圖片大小各異&#xff0c;排列也參差不齊。若不加以調整&#xff0c;遇到要求嚴格的領導&#xff0c;可能會讓人頗為頭疼。 而這款工具能夠統…

工業巡檢機器人 —— 機器人市場的新興增長引擎

摘要 在機器人產業蓬勃發展的當下&#xff0c;不同類型機器人的市場表現差異顯著。工業機械臂雖市場規模龐大&#xff0c;但已趨近飽和&#xff0c;陷入紅海競爭&#xff1b;人形機器人因技術瓶頸仍多停留于實驗室階段&#xff0c;距離大規模商用尚有較長距離。與之形成鮮明對比…

Oracle where條件執行先后順序

Oracle where條件執行先后順序 在Oracle數據庫中&#xff0c;WHERE子句的條件執行順序通常是根據你在WHERE子句中指定的條件來決定的&#xff0c;而不是按照某種固定的順序執行的。當你編寫一個WHERE子句時&#xff0c;你可以包含多個條件&#xff0c;這些條件可以是邏輯運算符…

在Linux中使用 times函數 和 close函數 兩種方式 打印進程時間。

times函數用于獲取當前進程時間,其函數原型如下所示: #include <sys/times.h> clock_t times(struct tms *buf); //使用該函數需要包含頭文件<sys/times.h>。 函數參數和返回值含義如下: buf:times()會將當前進程時間信息存在一個 struct tms 結構體數據…

Python文字轉語音TTS庫示例(edge-tts)

1. 安裝 pip install edge-tts2. 命令行使用 # 生成語音文件 # -f:要轉換語音的文本文件,例如一個txt文件 # --text:指明要保存的mp3的文本 # --write-media:指明保存的mp3文件路徑 # --write-subtitles:指定輸出字幕/歌詞路徑 # --rate:調整語速,+50%加快了50% # --v…

Elasticsearch性能調優全攻略:從日志分析到集群優化

#作者&#xff1a;獵人 文章目錄 前言搜索慢查詢日志索引慢寫入日志性能調優之基本優化建議性能調優之索引寫入性能優化提升es集群寫入性能方法&#xff1a;性能調優之集群讀性能優化性能調優之搜索性能優化性能調優之GC優化性能調優之路由優化性能調優之分片優化 前言 es里面…

MongoDB從入門到實戰之Windows快速安裝MongoDB

前言 本章節的主要內容是在 Windows 系統下快速安裝 MongoDB 并使用 Navicat 工具快速連接。 MongoDB從入門到實戰之MongoDB簡介 MongoDB從入門到實戰之MongoDB快速入門 MongoDB從入門到實戰之Docker快速安裝MongoDB 下載 MongoDB 安裝包 打開 MongoDB 官網下載頁面&…

Serverless,云計算3.0階段

Hi~各位讀者朋友們&#xff0c;感謝您閱讀本文&#xff0c;我是笠泱&#xff0c;本期簡單分享下Serverless。Serverless是一種云計算服務模式&#xff0c;為業務代碼提供運行環境及調度服務。開發者只需專注于編寫業務邏輯代碼&#xff0c;無需管理底層基礎設施&#xff08;如服…

eSearch:一款集截圖、OCR與錄屏于一體的多功能軟件

eSearch&#xff1a;一款集截圖、OCR與錄屏于一體的多功能軟件 軟件介紹 eSearch是一款專為Windows 10和11用戶設計的多功能軟件&#xff0c;集截圖、OCR文字識別、錄屏等功能于一體&#xff0c;且完全免費。其便捷版無需安裝&#xff0c;運行后最小化至托盤圖標&#xff0c;…

React學習———useContext和useReducer

useContext useContext是React的一個Hook&#xff0c;用于在函數組件中訪問上下文&#xff08;context&#xff09;的值。它可以幫助我們在組件樹中共享狀態&#xff0c;而不需要通過props一層層傳遞 特點 用于跨組件共享狀態需要配合React.createContext和Context.Provider…

安卓刷機模式詳解:Fastboot、Fastbootd、9008與MTK深刷

安卓刷機模式詳解&#xff1a;Fastboot、Fastbootd、9008與MTK深刷 一、刷機模式對比 1. Fastboot模式 簡介&#xff1a;傳統安卓底層刷機模式&#xff0c;通過USB連接電腦操作優點&#xff1a;支持大多數安卓設備&#xff0c;操作相對簡單缺點&#xff1a;需要設備進入特定…

HDFS的概述

HDFS組成構架&#xff1a; 注&#xff1a; NameNode&#xff08;nn&#xff09;&#xff1a;就是 Master&#xff0c;它是一個主管、管理者。 (1) 管理 HDFS 的名稱空間&#xff1b; (2) 配置副本策略。記錄某些文件應該保持幾個副本&#xff1b; (3) 管理數據塊&#xff0…

配置Spark環境

1.上傳spark安裝包到某一臺機器&#xff08;自己在finaShell上的機器&#xff09;。 2.解壓。 把第一步上傳的安裝包解壓到/opt/module下&#xff08;也可以自己決定解壓到哪里&#xff09;。對應的命令是&#xff1a;tar -zxvf 安裝包 -C /opt/module 3.重命名。進入/opt/mo…

Java筆記五

1 Math類 1.1 概述 tips&#xff1a;了解內容 查看API文檔&#xff0c;我們可以看到API文檔中關于Math類的定義如下&#xff1a; Math類所在包為java.lang包&#xff0c;因此在使用的時候不需要進行導包。并且Math類被final修飾了&#xff0c;因此該類是不能被繼承的。 Math…