封裝紅黑樹實現mysetmymap

1. 源碼分析

  • set實例化rb_tree時第二個模板參數給的是key,map實例化rb_tree時第?個模板參數給的是 pair<const key,T>,這樣一顆紅黑樹既可以實現key搜索場景的set,也可以實現key/value搜索場 景的map
  • 源碼里面模板參數是用T代表value,而內部寫的value_type不是日常 key/value場景中說的value,源碼中的value_type反而是紅黑樹結點中存儲的真實的數據的類型
  • 為什么set要傳兩個key?因為對于 map和set,find/erase時的函數參數都是Key,第一個模板參數是傳給find/erase等函數做形參的類型的。對于set兩個參數是一樣的,對于map不一樣了,map的insert是pair對象,但是find和ease的是Key對象

2. 模擬實現map和set

2.1 復用紅黑樹的框架

  • key參數就用K,value參數就用V,紅黑樹中的數據類型使用T
  • 因為RBTree實現了泛型不知道T參數導致是K,還是pair<K,V>,那么insert內部進行插入邏輯比較時,就沒辦法進行比較,因為pair的默認支持的是key和value一起參與比較,我們需要時的任何時候只比較key,所以在map和set層分別實現一個MapKeyOfTSetKeyOfT仿函數傳給RBTree的KeyOfT,然后RBTree中通過KeyOfT仿函數取出T類型對象中的key,再進行比較
//myset.h
namespace smc{template <class K>
class set
{struct SetKeyOfT {const K& operator()(const K& key){return key;}};
public:
private:RBTree<K,const K, SetKeyOfT> _rbtree;
};}
//mymap.h
namespace smc
{template<class K,class V>class map {struct MapKeyOfT{ const K& operator()(const pair<K, V>& kv){return kv.first;}};public:private:RBTree<K, pair<const K, V>, MapKeyOfT> _rbtree;};
}
// RBTree.h// 實現步驟: 
// 1、實現紅黑樹 
// 2、封裝map和set框架,解決KeyOfT 
// 3、iterator 
// 4、const_iterator 
// 5、key不支持修改的問題 
// 6、operator[] 
enum Colour
{RED,BLACK
};
template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& data): _data(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};
template<class K, class T, class KeyOfT>
class RBTree
{
private:typedef RBTreeNode<T> Node;Node* _root = nullptr;public:bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(data);Node* newnode = cur;// 新增結點。顏?給紅? cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//...return true;}
}

2.2 支持iterator的實現

  • terator實現的框架跟list的iterator思路是一致的,用個類型封裝結點的指針,再通過重載運算符實現,迭代器像指針一樣訪問的行為
  • 這里的難點是operator++和operator--的實現。之前使?部分,map和set的迭代器走的是中序遍歷,左子樹->根結點->右子樹,那么begin()會返回中序第一個結點的iterator也就是10所在結點的迭代器
  • 迭代器++時,如果it指向的結點的右子樹不為空,代表當前結點已經訪問完了,要訪問下一個結點 是右子樹的中序第一個,一棵樹中序第一個是最左結點,所以直接找右子樹的最左結點即可
  • 迭代器++時,如果it指向的結點的右子樹為空,代表當前結點已經訪問完了且當前結點所在的子樹也訪問完了,要往上走。而且如果當前it是其父節點的右子樹,表示這整個子樹都完了,就要去找it的祖先節點
  1. 如果當前結點是父親的左,根據中序左子樹->根結點->右子樹,那么下一個訪問的結點就是當前結 點的父親;如下圖:it指向25,25右為空,25是30的左,所以下一個訪問的結點就是30
  2. 如果當前結點是父親的右,根據中序左子樹->根結點->右子樹,當前當前結點所在的子樹訪問完 了,當前結點所在父親的子樹也訪問完了,那么下一個訪問的需要繼續往根的祖先中去找,直到找 到孩?是父親左的那個祖先就是中序要問題的下一個結點。如下圖:it指向15,15右為空,15是10 的右,15所在子樹話訪問完了,10所在子樹也訪問完了,繼續往上找,10是18的左,那么下一個 訪問的結點就是18
  • end()如何表示?如下圖:當it指向50時,++it時,50是40的右,40是30的右,30是18的右,18 到根沒有父親,沒有找到孩?是父親左的那個祖先,這是父親為空了,那我們就把it中的結點指針置為nullptr,用nullptr去充當end。需要注意的是stl源碼空,紅黑樹增加了一個哨兵位頭結點 做為end(),這哨兵位頭結點和根互為父親,左指向最左結點,右指向最右結點。相比我們用?nullptr作為end(),只是--end()判斷到結點時空,特殊處理一下,讓迭代器結點指向最右結點。具體參考迭代器--實現
  • 迭代器--的實現跟++的思路完全類似,邏輯正好反過來即可,訪問順序是右子樹->根結點-> 左子樹
  • set的iterator也不支持修改,我們把set的第?個模板參數改成const K即可, 即RBTree<K,pair<K,V>,SetKeyOfT> _rbtree
  • map的iterator不支持修改key但是可以修改value,我們把map的第二個模板參數pair的第一個參數改成const K即可,即RBTree<K, pair<const K, V>, MapKeyOfT> _rbtree;

2.3 map支持[ ]

map要支持[ ]主要需要修改insert返回值支持,修改RBtree中的insert返回值為

pair<Iterator,bool> Insert(const T& data)

2.4 smc::set和smc::map代碼實現

dwaekkiyo/test - Gitee.com

//set.h
namespace smc
{template <class K>class set{struct SetKeyOfT {const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K,const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K,const K, SetKeyOfT>::ConstIterator const_iterator;iterator begin(){return _rbtree.begin();}iterator end(){return _rbtree.end();}const_iterator begin() const{return _rbtree.begin();}const_iterator end() const{return _rbtree.end();}pair<iterator,bool> insert(const K& key){return _rbtree.Insert(key);}iterator Find(const K& key){_rbtree.Find(key);}private:RBTree<K,const K, SetKeyOfT> _rbtree;};
}
//map.h
namespace smc
{template<class K,class V>class map {struct MapKeyOfT{ const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;iterator begin(){return _rbtree.begin();}iterator end(){return _rbtree.end();}const_iterator begin() const{return _rbtree.Begin();}const_iterator end() const{return _rbtree.End();}pair<iterator,bool> insert(const pair<K,V> kv){return _rbtree.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _rbtree.Insert({ key,V() });return ret.first->second;}iterator Find(const K& key){_rbtree.Find(key);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _rbtree;};void test_map(){map<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左邊" });dict.insert({ "right", "右邊" });dict["left"] = "左邊,剩余";dict["insert"] = "插入";dict["string"];map<string, string>::iterator it = dict.begin();while (it != dict.end()){// 不能修改first,可以修改second //it->first += 'x';it->second += 'x';cout << it->first << ":" << it->second << endl;++it;}cout << endl;}
}
//RBTree.h
#pragma onceenum Colour
{Red,Black
};template <class T>
struct RBTreeNode {T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Colour _col;RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template <class T,class Ref,class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T,Ref,Ptr> Self;Node* _node;Node* _root;RBTreeIterator(Node* node,Node* root):_node(node),_root(root){}Self& operator++(){if (_node->_right){Node* minleft = _node->_right;while (minleft->_left){minleft = minleft->_left;}_node = minleft;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--(){if (_node == nullptr){//在end位置Node* rightmost = _root;while (rightmost && rightmost->_right){rightmost = rightmost->_right;}_node = rightmost;}else if (_node->_left){// 左子樹不為空,中序左子樹最后?個 Node* rightmost = _node->_left;while (rightmost->_right){rightmost = rightmost->_right;}_node = rightmost;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}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;}};template<class K, class T,class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef RBTreeIterator<T,T&,T*> Iterator;typedef RBTreeIterator<T,const T&,const T*> ConstIterator;Iterator begin(){Node* cur = _root;while (cur&& cur->_left){cur = cur->_left;}return Iterator(cur,_root);}Iterator end(){return Iterator(nullptr,_root);}ConstIterator begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return ConstIterator(cur, _root);}ConstIterator end() const{return ConstIterator(nullptr, _root);}pair<Iterator,bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = Black;return make_pair(Iterator(_root,_root),true);}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;while (cur){if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else{return make_pair(Iterator(cur,_root),false);}}cur = new Node(data);Node* newnode = cur;cur->_col = Red;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//如果父親結點是紅的while (parent && parent->_col == Red){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//   g// p   u//Node* uncle = grandfather->_right;//u存在且為紅if (uncle && uncle->_col == Red){//變色parent->_col = Black;uncle->_col = Black;grandfather->_col = Red;//繼續向上處理cur = grandfather;parent = cur->_parent;}else{//uncle不存在,或者存在且為黑if (cur == parent->_left){// 旋轉+變色//    g//  p   u//cRotateR(grandfather);parent->_col = Black;grandfather->_col = Red;}else{// 雙旋轉+變色//    g//  p   u//    cRotateL(parent);RotateR(grandfather);cur->_col = Black;//cur做了這棵樹的根grandfather->_col = Red;}break;}}else{//	 g// u   p//Node* uncle = grandfather->_left;// 叔叔存在且為紅 變色即可if (uncle && uncle->_col == Red){parent->_col = Black;uncle->_col = Black;grandfather->_col = Red;//繼續向上處理cur = grandfather;parent = cur->_parent;}else//uncle不存在,或者存在且為黑{// 旋轉+變色//    g//  u   p//         cif (cur == parent->_right){RotateL(grandfather);parent->_col = Black;grandfather->_col = Red;}else{// 雙旋轉+變色//    g//  u   p//    cRotateR(parent);RotateL(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}}_root->_col = Black;//不返回cur是因為旋轉后cur可能已經不在原位//所以newnode記錄新節點并返回//return make_pair(Iterator(cur,_root),true);return make_pair(Iterator(newnode,_root),true);}bool Check(Node* root, int BlackNum, const int refNum){// blackNum 根到當前結點的黑結點的數量if (root == nullptr){// 前序遍歷走到空時,意味著一條路徑走完了if (BlackNum != refNum){cout << "存在黑色結點的數量不相等的路徑" << endl;return false;}return true;}//檢查父親if (root->_col == Red && root->_parent->_col == Red){cout << root->_kv.first << "存在連續的紅結點" << endl;return false;}if (root->_col == Black){BlackNum++;}return Check(root->_left, BlackNum, refNum) && Check(root->_right, BlackNum, refNum);}bool IsBalance(){if (_root == nullptr){return true;}if (_root->_col == Red){return false;}Node* cur = _root;int refNum = 0; // 參考值 while (cur){if (cur->_col == Black)++refNum;cur = cur->_left;}return Check(_root, 0, refNum);}Iterator Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return Iterator(cur,_root);}}return end();}void InOrder(){_InOrder(_root);cout << endl;}int Height(){return _Height(_root);}int Size(){return _Size(_root);}protected:int _Size(Node* root){if (root == nullptr)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppNode = parent->_parent;subL->_right = parent;parent->_parent = subL;//if (ppNode == nullptr)if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}
private:Node* _root = nullptr;
};

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

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

相關文章

以OWTB為核心以客戶為基礎的三方倉運配一體化平臺分析V0.2

一、系統概述以OWTB&#xff08;Order-Warehouse-Transportation-Billing&#xff0c;訂單-倉儲-運輸-結算&#xff09;為核心的三方倉運配一體化平臺&#xff0c;是專為第三方物流企業打造的深度定制化解決方案。該平臺以第三方倉運配為主線&#xff0c;以多客戶/多SKU/個性化…

技術框架之腳手架實現

一、 序言在日常的企業級Java開發中&#xff0c;我們經常會發現自己在重復地做著一些項目初始化工作&#xff1a;創建相似的項目結構、引入一堆固定的依賴包、編寫通用的配置文件、拷貝那些幾乎每個項目都有的基礎工具類和日志配置。這些工作不僅枯燥乏味&#xff0c;而且容易出…

小迪安全v2023學習筆記(七十七講)—— 業務設計篇隱私合規檢測重定向漏洞資源拒絕服務

文章目錄前記WEB攻防——第七十七天業務設計篇&隱私合規檢測&URL重定向&資源拒絕服務&配合項目隱私合規 - 判斷規則&檢測項目介紹案例演示URL重定向 - 檢測判斷&釣魚配合介紹黑盒測試看業務功能看參數名goole語法搜索白盒測試跳轉URL繞過思路釣魚配合資…

用AI做旅游攻略,真能比人肉整理靠譜?

大家好&#xff0c;我是極客團長&#xff01; 作為一個沉迷研究 “AI 工具怎么滲透日常生活” 的科技博主&#xff0c;我開了個 AI 解決生活小事系列。 前兩期聊了用 AI 寫新聞博客、扒商業報告&#xff0c;后臺一堆人催更&#xff1a;能不能搞點接地氣的&#xff1f;比如&am…

Axure RP 9 Mac 交互原型設計

原文地址&#xff1a;Axure RP 9 Mac 交互原型設計 安裝教程 Axure RP 9是一款功能強大的原型設計和協作工具。 它不僅能夠幫助用戶快速創建出高質量的原型設計&#xff0c;還能促進團隊成員之間的有效協作&#xff0c;從而極大地提高數字產品開發的效率和質量。 擁有直觀易…

多線程——線程狀態

目錄 1.線程的狀態 1.1 NEW 1.2 RUNNABLE 1.3 BLOCKED 1.4 WAITING 1.5 TIMED_WAITING 1.6 TERMINATED 2.線程狀態的相互轉換 在上期的學習中&#xff0c;已經理解線程的啟動&#xff08;start()&#xff09;、休眠&#xff08;sleep()&#xff09;、中斷&#xff08;i…

IMX6ULL的設備樹文件簡析

先分析一個完整的設備樹&#xff0c;是怎么表達各種外設信息的。以imux6ull開發板為例進行說明。這個文件里就一個設備信息才這么點內容&#xff0c;是不是出問題了&#xff1f;當然不是&#xff0c;我們知道dts文件是可包含的&#xff0c;所以&#xff0c;最終形成的一個完整文…

【ARM】PACK包管理

1、 文檔目標對 pack 包的管理有更多的了解。2、 問題場景客戶在安裝了過多的 pack 包導致軟件打開比較慢&#xff0c;各種 pack 包顏色的區別&#xff0c;及圖標不同。3、軟硬件環境1&#xff09;、軟件版本&#xff1a;Keil MDK 5.392&#xff09;、電腦環境&#xff1a;Wind…

【Kubernetes】知識點4

36. 說明K8s中Pod級別的Graceful Shutdown。答&#xff1a;Graceful Shutdown&#xff08;優雅關閉&#xff09;是指當 Pod 需要終止時&#xff0c;系統給予運行中的容器一定的時間來等待業務的應用的正常關閉&#xff08;如保存數據、關閉連接、釋放資源等&#xff09;&#x…

Paraverse平行云實時云渲染助力第82屆威尼斯電影節XR沉浸式體驗

今年&#xff0c;Paraverse平行云實時云渲染平臺LarkXR&#xff0c;為享有盛譽的第82屆威尼斯國際電影節&#xff08;8月27日至9月6日&#xff09;帶來沉浸式體驗。 LarkXR助力我們的生態伙伴FRENCH TOUCH FACTORY&#xff0c;實現ITHACA容積視頻的XR交互演示&#xff0c;從意大…

大數據開發計劃表(實際版)

太好了&#xff01;我將為你生成一份可打印的PDF版學習計劃表&#xff0c;并附上項目模板與架構圖示例&#xff0c;幫助你更直觀地執行計劃。 由于當前環境無法直接生成和發送文件&#xff0c;我將以文本格式為你完整呈現&#xff0c;你可以輕松復制到Word或Markdown中&#xf…

GitLab 18.3 正式發布,更新多項 DevOps、CI/CD 功能【二】

沿襲我們的月度發布傳統&#xff0c;極狐GitLab 發布了 18.3 版本&#xff0c;該版本帶來了通過直接轉移進行遷移、CI/CD 作業令牌的細粒度權限控制、自定義管理員角色、Kubernetes 1.33 支持、通過 API 讓流水線執行策略訪問 CI/CD 配置等幾十個重點功能的改進。下面是對部分重…

Docker學習筆記(二):鏡像與容器管理

Docker 鏡像 最小的鏡像 hello-world 是 Docker 官方提供的一個鏡像&#xff0c;通常用來驗證 Docker 是否安裝成功。 先通過 docker pull 從 Docker Hub 下載它。 [rootdocker ~]# docker pull hello-world Using default tag: latest latest: Pulling from library/hello-wor…

STM32F103C8T6開發板入門學習——寄存器和庫函數介紹

學習目標&#xff1a;STM32F103C8T6開發板入門學習——寄存器和庫函數介紹學習內容&#xff1a; 1. 寄存器介紹 1.1 存儲器映射 存儲器本身無固有地址&#xff0c;是具有特定功能的內存單元。它的地址是由芯片廠商或用戶分配&#xff0c;給存儲器分配地址的過程就叫做存儲區映射…

【CouponHub項目開發】使用RocketMQ5.x實現延時修改優惠券狀態,并通過使用模板方法模式重構消息隊列發送功能

在上個章節中我實現了創建優惠券模板的功能&#xff0c;但是&#xff0c;優惠券總會有過期時間&#xff0c;我們怎么去解決到期自動修改優惠券狀態這樣一個功能呢&#xff1f;我們可以使用RocketMQ5.x新出的任意定時發送消息功能來解決。 初始方案&#xff1a;首先在創建優惠券…

Claude Code SDK 配置Gitlab MCP服務

一、MCP配置前期準備 &#xff08;一&#xff09;創建個人令牌/群組令牌 我這里是創建個人令牌&#xff0c;去到首頁左上角&#xff0c;點擊頭像——>偏好設置——>訪問令牌——>添加新令牌 &#xff08;二&#xff09;配置mcp信息 去到魔塔社區&#xff0c;點擊mc…

Eclipse 常用搜索功能匯總

Eclipse 常用搜索功能匯總 Eclipse 提供了多種搜索功能&#xff0c;幫助開發者快速定位代碼、文件、類、方法、API 等資源。以下是詳細的使用方法和技巧。 一、常用搜索快捷鍵快捷鍵功能描述Ctrl H打開全局搜索對話框&#xff0c;支持文件、Java 代碼、任務等多種搜索。Ctrl …

關于Spring的一些理解

Spring整體結構&#xff1a;Spring實際運行場景&#xff1a;基礎 Spring啟動過程 傳統Spring&#xff1a; &#xff08;1&#xff09;初始化準備階段 &#xff08;2&#xff09;容器創建與注入 &#xff08;3&#xff09;Bean工廠后置處理 &#xff08;4&#xff09;Bean工廠后…

Windows右下角系統托盤圖標快速顯示或隱藏

系統托盤指的是Windows電腦桌面右下角的區域&#xff0c;包括時間、wifi&#xff08;網絡&#xff09;、音量、電源、輸入法、一些程序/應用等。啟動了應用后&#xff0c;Windows會把部分應用的圖標顯示或隱藏在系統托盤區。我們可以根據需要快速顯示或隱藏相關應用&#xff0c…

Kotlin編程學習記錄2

Kotlin編程學習記錄2——條件與循環 條件語句&#xff1a;if 與 when ? Kotlin 的控制流把“表達式優先”作為設計原則——if、when 不只是控制語句&#xff0c;都可以作為表達式使用并返回值&#xff0c;這影響了日常代碼風格&#xff08;更函數式、可組合&#xff09;。筆…