C++用一棵紅黑樹同時封裝出set與map的實現(xiàn)代碼
前言
我們了解到set中存儲的一般為鍵K即可,而map存儲的一般都是鍵值對KV,也就是說他們結(jié)構(gòu)是不同的,那么我們?nèi)绾尾拍苡靡活w紅黑樹同時封裝出set與map兩種容器呢?
簡單的說就是模板的使用,對于set存儲的<K,K>,對于map存儲的是<K,pair<K,V>>;
那么紅黑樹我們就可以使用模板,比如RBTree<K,T>,T就是這個模板類,當set使用時就是K,當map使用時就是pair。
那么接下來我們具體地來研究下STL庫中是怎樣實現(xiàn)的,并且進行模擬實現(xiàn)。
1.紅黑樹模板參數(shù)的控制
template<class K, class T> class RBTree
那么對于set:
template<class K> class set { public: //... private: RBTree<K, K> _t; };
對于map:
template<class K, class V> class map { public: //... private: RBTree<K, pair<K, V>> _t; };
即:
思考:既然對于map來說pair中有K,那么是不是可以將第一個模板參數(shù)省略呢?
- 對于set容器來說:可以,因為set傳入紅黑樹的第二個參數(shù)與第一個參數(shù)是一樣的;
- 對于map容器來說:不行,因為map容器所提供的接口當中有些是只要求給出鍵值Key的,比如find和erase。
2.紅黑樹節(jié)點的定義
//紅黑樹結(jié)點的定義 template<class T> struct RBTreeNode { //三叉鏈 RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; //存儲的數(shù)據(jù) T _data; //結(jié)點的顏色 int _col; //構(gòu)造函數(shù) RBTreeNode(const T& data) : _left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _col(RED) {} };
對于模板參數(shù)T來說,set使用就是K,map使用就是pair<K,V>,對應著set與map中節(jié)點存儲的數(shù)據(jù)類型。
3.pair的比較規(guī)則引出紅黑樹仿函數(shù)設計
紅黑樹是一棵二叉搜索樹,所以當我們尋找插入位置或者查找時一定會比較節(jié)點值之間的大小。
新插入節(jié)點值小于當前節(jié)點值,就往左走;
新插入節(jié)點值大于當前節(jié)點值,就往右走;
這是之前學習二叉搜索樹最基本的特性,那么問題來了,對于map而言,節(jié)點值存儲的是pair<K,V>,可是pair是依據(jù)什么來決定自身的大小呢?first?second?還是什么?
我們來看一下STL庫中對pair比較大小的定義:
可我們期望的比較規(guī)則是這樣么?
很明顯不是,我們期望的是set與map都只依據(jù)Key來比較大小。
那么我們就需要想辦法構(gòu)造一個我們自己比較的方式出來。
首先比較的是Key,所以我們需要想辦法取出Key,對于set而言那就是Key,對于map而言是pair的first,所以我們可以在紅黑樹中設計仿函數(shù)來統(tǒng)一設計,然后在set和map中具體實現(xiàn)即可。
set:
template<class K> class set { //仿函數(shù) struct SetKeyOfT { const K& operator()(const K& key) //返回鍵值Key { return key; } }; public: //... private: RBTree<K, K, SetKeyOfT> _t; };
map:
template<class K, class V> class map { //仿函數(shù) struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) //返回鍵值對當中的鍵值Key { return kv.first; } }; public: //... private: RBTree<K, pair<K, V>, MapKeyOfT> _t; };
RBTree:
template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //結(jié)點的類型 public: //... private: Node* _root; }
即:
在實現(xiàn)紅黑樹時,只需要創(chuàng)建這個KeyOfT類對象,就相當于實例化了對應的set或map中實際的方法,比如:
//查找函數(shù) iterator Find(const K& key) { KeyOfT kot; Node* cur = _root; while (cur) { if (key < kot(cur->_data)) //key值小于該結(jié)點的值 { cur = cur->_left; //在該結(jié)點的左子樹當中查找 } else if (key > kot(cur->_data)) //key值大于該結(jié)點的值 { cur = cur->_right; //在該結(jié)點的右子樹當中查找 } else //找到了目標結(jié)點 { return iterator(cur); //返回該結(jié)點 } } return end(); //查找失敗 }
所以對于紅黑樹來說,所有對應的需要比較的部分我們都需要進行修改。
4.紅黑樹的正向迭代器
紅黑樹的正向迭代器實際上就是對結(jié)點指針進行了封裝,因此在正向迭代器當中實際上就只有一個成員變量,那就是正向迭代器所封裝結(jié)點的指針。
4.1迭代器的定義
//正向迭代器 template<class T, class Ref, class Ptr> struct __TreeIterator { typedef RBTreeNode<T> Node; //結(jié)點的類型 typedef __TreeIterator<T, Ref, Ptr> Self; //正向迭代器的類型 Node* _node; //正向迭代器所封裝結(jié)點的指針 };
4.2迭代器的構(gòu)造
我們通過一個結(jié)點的指針便可以構(gòu)造出一個正向迭代器。
//構(gòu)造函數(shù) __TreeIterator(Node* node) :_node(node) //根據(jù)所給結(jié)點指針構(gòu)造一個正向迭代器 {}
4.3重載解引用操作符 *
當對正向迭代器進行解引用操作時,我們直接返回對應結(jié)點數(shù)據(jù)的引用即可。
Ref operator*() { return _node->_data; //返回結(jié)點數(shù)據(jù)的引用 }
4.4重載箭頭操作符 ->
當對正向迭代器進行->操作時,我們直接返回對應結(jié)點數(shù)據(jù)的指針即可。
Ptr operator->() { return &_node->_data; //返回結(jié)點數(shù)據(jù)的指針 }
注意:這里與鏈表List部分的->操作符重載一樣,返回的是地址,即使用時為了可讀性省略了一個->。
4.5重載 == 和 != 操作符
實現(xiàn)時直接判斷兩個迭代器所封裝的結(jié)點是否是同一個即可。
//判斷兩個正向迭代器是否不同 bool operator!=(const Self& s) const { return _node != s._node; //判斷兩個正向迭代器所封裝的結(jié)點是否是同一個 } //判斷兩個正向迭代器是否相同 bool operator==(const Self& s) const { return _node == s._node; //判斷兩個正向迭代器所封裝的結(jié)點是否是同一個 }
4.6重載 ++、-- 操作符
之前鏈表List迭代器的 ++ 操作符重載的邏輯非常簡單,只需要 node=node->next即可,但是對于一棵二叉搜索樹而言顯然不會這么簡單。
那么按照搜索的邏輯,我們知道++應該得到的是中序遍歷的后一個節(jié)點。
根據(jù)圖像我們可以推導出 ++ 的邏輯:
- 如果當前結(jié)點的右子樹不為空,則
++
操作后應該找到其右子樹當中的最左結(jié)點。 - 如果當前結(jié)點的右子樹為空,則
++
操作后應該在該結(jié)點的祖先結(jié)點中,找到孩子不在父親右的祖先。
//前置++ Self operator++() { if (_node->_right) //結(jié)點的右子樹不為空 { //尋找該結(jié)點右子樹當中的最左結(jié)點 Node* left = _node->_right; while (left->_left) { left = left->_left; } _node = left; //++后變?yōu)樵摻Y(jié)點 } else //結(jié)點的右子樹為空 { //尋找孩子不在父親右的祖先 Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur == parent->_right) { cur = parent; parent = parent->_parent; } _node = parent; //++后變?yōu)樵摻Y(jié)點 } return *this; }
那么同樣的對于 -- 操作符的邏輯如下:
- 如果當前結(jié)點的左子樹不為空,則
--
操作后應該找到其左子樹當中的最右結(jié)點。 - 如果當前結(jié)點的左子樹為空,則
--
操作后應該在該結(jié)點的祖先結(jié)點中,找到孩子不在父親左的祖先。
//前置-- Self operator--() { if (_node->_left) //結(jié)點的左子樹不為空 { //尋找該結(jié)點左子樹當中的最右結(jié)點 Node* right = _node->_left; while (right->_right) { right = right->_right; } _node = right; //--后變?yōu)樵摻Y(jié)點 } else //結(jié)點的左子樹為空 { //尋找孩子不在父親左的祖先 Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur == parent->_left) { cur = parent; parent = parent->_parent; } _node = parent; //--后變?yōu)樵摻Y(jié)點 } return *this; }
正向迭代器實現(xiàn)后,我們需要在紅黑樹的實現(xiàn)當中進行迭代器類型的typedef。
需要注意的是,為了讓外部能夠使用typedef后的正向迭代器類型iterator,我們需要在public區(qū)域進行typedef。
然后在紅黑樹當中實現(xiàn)成員函數(shù)begin和end:
- begin函數(shù)返回中序序列當中第一個結(jié)點的正向迭代器,即最左結(jié)點。
- end函數(shù)返回中序序列當中最后一個結(jié)點下一個位置的正向迭代器,這里直接用空指針構(gòu)造一個正向迭代器。
template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //結(jié)點的類型 public: typedef __TreeIterator<T, T&, T*> iterator; //正向迭代器 iterator begin() { //尋找最左結(jié)點 Node* left = _root; while (left&&left->_left) { left = left->_left; } //返回最左結(jié)點的正向迭代器 return iterator(left); } iterator end() { //返回由nullptr構(gòu)造得到的正向迭代器(不嚴謹) return iterator(nullptr); } private: Node* _root; //紅黑樹的根結(jié)點 };
但這樣實現(xiàn)的end()迭代器其實并不符合要求,因為如果對end()位置的迭代器--后,應該得到最后一個位置的正向迭代器,但這里很明顯無法實現(xiàn)。
實際上在STL庫中是這樣設計的:
在紅黑樹的根節(jié)點新增一個頭節(jié)點,這個頭結(jié)點的左孩子為最左節(jié)點,右孩子為最右節(jié)點。
在這樣的結(jié)構(gòu)下,正向迭代器的begin()只需用頭節(jié)點的左孩子構(gòu)造即可,反向迭代器的rbegin()只需用頭結(jié)點的右孩子構(gòu)造出一個正向迭代器然后再對該正向迭代器進行封裝,得到反向迭代器即可。end和rend()
但是如果紅黑樹加入了這個頭節(jié)點,就會改變之前我們實現(xiàn)的紅黑樹的很多邏輯,這里就不實現(xiàn)了。
5.紅黑樹的反向迭代器
還記得我們之前學習過的反向迭代器的實現(xiàn)么?C++ 反向迭代器模擬實現(xiàn)_C 語言_腳本之家 (jb51.net)
反向迭代器需不需要我們從零開始寫呢?還是和適配器一樣,利用普通迭代器的結(jié)構(gòu)滿足反向迭代器的特性即可?這樣是不是比較方便?
- rbegin()相當于end()
- rend()相當于begin()
- 反向迭代器++相當于正向迭代器--
- 其他操作* != ->和正向迭代器相同
//反向迭代器---迭代器適配器 template<class Iterator> struct ReverseIterator { typedef ReverseIterator<Iterator> Self; //反向迭代器的類型 //限定符不能區(qū)分靜態(tài)變量和類型,所以前面可以加上typename標識為類型 typedef typename Iterator::reference Ref; //結(jié)點指針的引用 typedef typename Iterator::pointer Ptr; //結(jié)點指針 Iterator _it; //反向迭代器所封裝的正向迭代器 //構(gòu)造函數(shù) ReverseIterator(Iterator it) :_it(it) //根據(jù)所給正向迭代器構(gòu)造一個反向迭代器 {} Ref operator*() { return *_it; //通過調(diào)用正向迭代器的operator*返回結(jié)點數(shù)據(jù)的引用 } Ptr operator->() { return _it.operator->(); //通過調(diào)用正向迭代器的operator->返回結(jié)點數(shù)據(jù)的指針 } //前置++ Self& operator++() { --_it; //調(diào)用正向迭代器的前置-- return *this; } //前置-- Self& operator--() { ++_it; //調(diào)用正向迭代器的前置++ return *this; } bool operator!=(const Self& s) const { return _it != s._it; //調(diào)用正向迭代器的operator!= } bool operator==(const Self& s) const { return _it == s._it; //調(diào)用正向迭代器的operator== } };
需要注意的是,反向迭代器只接收了一個模板參數(shù),即正向迭代器的類型,也就是說,反向迭代器不知道結(jié)點的引用類型和結(jié)點的指針類型,因此我們需要在正向迭代器當中對這兩個類型進行typedef,這樣反向迭代器才能通過正向迭代器獲取結(jié)點的引用類型和結(jié)點的指針類型。
//正向迭代器 template<class T, class Ref, class Ptr> struct __TreeIterator { typedef Ref reference; //結(jié)點指針的引用 typedef Ptr pointer; //結(jié)點指針 };
反向迭代器的rbegin():紅黑樹的最右節(jié)點;
反向迭代器的rend():空指針構(gòu)造;
template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //結(jié)點的類型 public: typedef ReverseIterator<iterator> reverse_iterator; //反向迭代器 reverse_iterator rbegin() { //尋找最右結(jié)點 Node* right = _root; while (right&&right->_right) { right = right->_right; } //返回最右結(jié)點的反向迭代器 return reverse_iterator(iterator(right)); } reverse_iterator rend() { //返回由nullptr構(gòu)造得到的反向迭代器(不嚴謹) return reverse_iterator(iterator(nullptr)); } private: Node* _root; //紅黑樹的根結(jié)點 };
注意:此時迭代器指向的值可以被修改,而在set中我們存儲的是<K,K>,map中存儲的是<K,pair<K,V>>,為了防止二叉樹遭到破壞,所以我們需要進行類似這樣的處理:
- set<K,const K>;
- map<K,pair<const K,V>>;
6.完整代碼
RBTree.h
#pragma once //枚舉定義結(jié)點的顏色 enum Colour { RED, BLACK }; //紅黑樹結(jié)點的定義 template<class T> struct RBTreeNode { //三叉鏈 RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; //存儲的數(shù)據(jù) T _data; //結(jié)點的顏色 int _col; //紅/黑 //構(gòu)造函數(shù) RBTreeNode(const T& data) : _left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _col(RED) {} }; //正向迭代器 template<class T, class Ref, class Ptr> struct __TreeIterator { typedef Ref reference; //結(jié)點指針的引用 typedef Ptr pointer; //結(jié)點指針 typedef RBTreeNode<T> Node; //結(jié)點的類型 typedef __TreeIterator<T, Ref, Ptr> Self; //正向迭代器的類型 Node* _node; //正向迭代器所封裝結(jié)點的指針 //構(gòu)造函數(shù) __TreeIterator(Node* node) :_node(node) //根據(jù)所給結(jié)點指針構(gòu)造一個正向迭代器 {} Ref operator*() { return _node->_data; //返回結(jié)點數(shù)據(jù)的引用 } Ptr operator->() { return &_node->_data; //返回結(jié)點數(shù)據(jù)的指針 } //判斷兩個正向迭代器是否不同 bool operator!=(const Self& s) const { return _node != s._node; //判斷兩個正向迭代器所封裝的結(jié)點是否是同一個 } //判斷兩個正向迭代器是否相同 bool operator==(const Self& s) const { return _node == s._node; //判斷兩個正向迭代器所封裝的結(jié)點是否是同一個 } //前置++ Self operator++() { if (_node->_right) //結(jié)點的右子樹不為空 { //尋找該結(jié)點右子樹當中的最左結(jié)點 Node* left = _node->_right; while (left->_left) { left = left->_left; } _node = left; //++后變?yōu)樵摻Y(jié)點 } else //結(jié)點的右子樹為空 { //尋找孩子不在父親右的祖先 Node* cur = _node; Node* parent = cur->_parent; while (parent && cur == parent->_right) { cur = parent; parent = parent->_parent; } _node = parent; //++后變?yōu)樵摻Y(jié)點 } return *this; } //前置-- Self operator--() { if (_node->_left) //結(jié)點的左子樹不為空 { //尋找該結(jié)點左子樹當中的最右結(jié)點 Node* right = _node->_left; while (right->_right) { right = right->_right; } _node = right; //--后變?yōu)樵摻Y(jié)點 } else //結(jié)點的左子樹為空 { //尋找孩子不在父親左的祖先 Node* cur = _node; Node* parent = cur->_parent; while (parent && cur == parent->_left) { cur = parent; parent = parent->_parent; } _node = parent; //--后變?yōu)樵摻Y(jié)點 } return *this; } }; //反向迭代器---迭代器適配器 template<class Iterator> struct ReverseIterator { typedef ReverseIterator<Iterator> Self; //反向迭代器的類型 typedef typename Iterator::reference Ref; //結(jié)點指針的引用 typedef typename Iterator::pointer Ptr; //結(jié)點指針 Iterator _it; //反向迭代器所封裝的正向迭代器 //構(gòu)造函數(shù) ReverseIterator(Iterator it) :_it(it) //根據(jù)所給正向迭代器構(gòu)造一個反向迭代器 {} Ref operator*() { return *_it; //通過調(diào)用正向迭代器的operator*返回結(jié)點數(shù)據(jù)的引用 } Ptr operator->() { return _it.operator->(); //通過調(diào)用正向迭代器的operator->返回結(jié)點數(shù)據(jù)的指針 } //前置++ Self& operator++() { --_it; //調(diào)用正向迭代器的前置-- return *this; } //前置-- Self& operator--() { ++_it; //調(diào)用正向迭代器的前置++ return *this; } bool operator!=(const Self& s) const { return _it != s._it; //調(diào)用正向迭代器的operator!= } bool operator==(const Self& s) const { return _it == s._it; //調(diào)用正向迭代器的operator== } }; //紅黑樹的實現(xiàn) template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //結(jié)點的類型 public: typedef __TreeIterator<T, T&, T*> iterator; //正向迭代器 typedef ReverseIterator<iterator> reverse_iterator; //反向迭代器 reverse_iterator rbegin() { //尋找最右結(jié)點 Node* right = _root; while (right && right->_right) { right = right->_right; } //返回最右結(jié)點的反向迭代器 return reverse_iterator(iterator(right)); } reverse_iterator rend() { //返回由nullptr構(gòu)造得到的反向迭代器(不嚴謹) return reverse_iterator(iterator(nullptr)); } iterator begin() { //尋找最左結(jié)點 Node* left = _root; while (left && left->_left) { left = left->_left; } //返回最左結(jié)點的正向迭代器 return iterator(left); } iterator end() { //返回由nullptr構(gòu)造得到的正向迭代器(不嚴謹) return iterator(nullptr); } //構(gòu)造函數(shù) RBTree() :_root(nullptr) {} //拷貝構(gòu)造 RBTree(const RBTree<K, T, KeyOfT>& t) { _root = _Copy(t._root, nullptr); } //賦值運算符重載(現(xiàn)代寫法) RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t) { swap(_root, t._root); return *this; //支持連續(xù)賦值 } //析構(gòu)函數(shù) ~RBTree() { _Destroy(_root); _root = nullptr; } //查找函數(shù) iterator Find(const K& key) { KeyOfT kot; Node* cur = _root; while (cur) { if (key < kot(cur->_data)) //key值小于該結(jié)點的值 { cur = cur->_left; //在該結(jié)點的左子樹當中查找 } else if (key > kot(cur->_data)) //key值大于該結(jié)點的值 { cur = cur->_right; //在該結(jié)點的右子樹當中查找 } else //找到了目標結(jié)點 { return iterator(cur); //返回該結(jié)點 } } return end(); //查找失敗 } //插入函數(shù) pair<iterator, bool> Insert(const T& data) { if (_root == nullptr) //若紅黑樹為空樹,則插入結(jié)點直接作為根結(jié)點 { _root = new Node(data); _root->_col = BLACK; //根結(jié)點必須是黑色 return make_pair(iterator(_root), true); //插入成功 } //1、按二叉搜索樹的插入方法,找到待插入位置 KeyOfT kot; Node* cur = _root; Node* parent = nullptr; while (cur) { if (kot(data) < kot(cur->_data)) //待插入結(jié)點的key值小于當前結(jié)點的key值 { //往該結(jié)點的左子樹走 parent = cur; cur = cur->_left; } else if (kot(data) > kot(cur->_data)) //待插入結(jié)點的key值大于當前結(jié)點的key值 { //往該結(jié)點的右子樹走 parent = cur; cur = cur->_right; } else //待插入結(jié)點的key值等于當前結(jié)點的key值 { return make_pair(iterator(cur), false); //插入失敗 } } //2、將待插入結(jié)點插入到樹中 cur = new Node(data); //根據(jù)所給值構(gòu)造一個結(jié)點 Node* newnode = cur; //記錄新插入的結(jié)點(便于后序返回) if (kot(data) < kot(parent->_data)) //新結(jié)點的key值小于parent的key值 { //插入到parent的左邊 parent->_left = cur; cur->_parent = parent; } else //新結(jié)點的key值大于parent的key值 { //插入到parent的右邊 parent->_right = cur; cur->_parent = parent; } //3、若插入結(jié)點的父結(jié)點是紅色的,則需要對紅黑樹進行調(diào)整 while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; //parent是紅色,則其父結(jié)點一定存在 if (parent == grandfather->_left) //parent是grandfather的左孩子 { Node* uncle = grandfather->_right; //uncle是grandfather的右孩子 if (uncle && uncle->_col == RED) //情況1:uncle存在且為紅 { //顏色調(diào)整 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //繼續(xù)往上處理 cur = grandfather; parent = cur->_parent; } else //情況2+情況3:uncle不存在 + uncle存在且為黑 { if (cur == parent->_left) { RotateR(grandfather); //右單旋 //顏色調(diào)整 grandfather->_col = RED; parent->_col = BLACK; } else //cur == parent->_right { RotateLR(grandfather); //左右雙旋 //顏色調(diào)整 grandfather->_col = RED; cur->_col = BLACK; } break; //子樹旋轉(zhuǎn)后,該子樹的根變成了黑色,無需繼續(xù)往上進行處理 } } else //parent是grandfather的右孩子 { Node* uncle = grandfather->_left; //uncle是grandfather的左孩子 if (uncle && uncle->_col == RED) //情況1:uncle存在且為紅 { //顏色調(diào)整 uncle->_col = parent->_col = BLACK; grandfather->_col = RED; //繼續(xù)往上處理 cur = grandfather; parent = cur->_parent; } else //情況2+情況3:uncle不存在 + uncle存在且為黑 { if (cur == parent->_left) { RotateRL(grandfather); //右左雙旋 //顏色調(diào)整 cur->_col = BLACK; grandfather->_col = RED; } else //cur == parent->_right { RotateL(grandfather); //左單旋 //顏色調(diào)整 grandfather->_col = RED; parent->_col = BLACK; } break; //子樹旋轉(zhuǎn)后,該子樹的根變成了黑色,無需繼續(xù)往上進行處理 } } } _root->_col = BLACK; //根結(jié)點的顏色為黑色(可能被情況一變成了紅色,需要變回黑色) return make_pair(iterator(newnode), true); //插入成功 } private: //拷貝樹 Node* _Copy(Node* root, Node* parent) { if (root == nullptr) { return nullptr; } Node* copyNode = new Node(root->_data); copyNode->_parent = parent; copyNode->_left = _Copy(root->_left, copyNode); copyNode->_right = _Copy(root->_right, copyNode); return copyNode; } //析構(gòu)函數(shù)子函數(shù) void _Destroy(Node* root) { if (root == nullptr) { return; } _Destroy(root->_left); _Destroy(root->_right); delete root; } //左單旋 void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* parentParent = parent->_parent; //建立subRL與parent之間的聯(lián)系 parent->_right = subRL; if (subRL) subRL->_parent = parent; //建立parent與subR之間的聯(lián)系 subR->_left = parent; parent->_parent = subR; //建立subR與parentParent之間的聯(lián)系 if (parentParent == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } } //右單旋 void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* parentParent = parent->_parent; //建立subLR與parent之間的聯(lián)系 parent->_left = subLR; if (subLR) subLR->_parent = parent; //建立parent與subL之間的聯(lián)系 subL->_right = parent; parent->_parent = subL; //建立subL與parentParent之間的聯(lián)系 if (parentParent == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subL; } else { parentParent->_right = subL; } subL->_parent = parentParent; } } //左右雙旋 void RotateLR(Node* parent) { RotateL(parent->_left); RotateR(parent); } //右左雙旋 void RotateRL(Node* parent) { RotateR(parent->_right); RotateL(parent); } Node* _root=nullptr; //紅黑樹的根結(jié)點 };
MySet.h
#pragma once #include "RBTree.h" namespace MySet { template<class K> class set { //仿函數(shù) struct SetKeyOfT { const K& operator()(const K& key) //返回鍵值Key { return key; } }; public: typedef typename RBTree<K, const K, SetKeyOfT>::iterator iterator; //正向迭代器 typedef typename RBTree<K, const K, SetKeyOfT>::reverse_iterator reverse_iterator; //反向迭代器 iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } //插入函數(shù) pair<iterator, bool> insert(const K& key) { return _t.Insert(key); } //查找函數(shù) iterator find(const K& key) { return _t.Find(key); } private: RBTree<K, const K, SetKeyOfT> _t; }; void test_set1() { set<int> s; int a[] = { 4,2,6,1,3,5,15,7,16,14 }; for (auto e : a) { s.insert(e); } set<int>::iterator it = s.begin(); while (it != s.end()) { cout << *it << " "; ++it; } cout << endl; } }
MyMap.h
#pragma once #include "RBTree.h" namespace MyMap { template<class K, class V> class map { //仿函數(shù) struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) //返回鍵值對當中的鍵值Key { return kv.first; } }; public: typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator; //正向迭代器 typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::reverse_iterator reverse_iterator; //反向迭代器 iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } //插入函數(shù) pair<iterator, bool> insert(const pair<const K, V>& kv) { return _t.Insert(kv); } //[]運算符重載函數(shù) V& operator[](const K& key) { pair<iterator, bool> ret = insert(make_pair(key, V())); iterator it = ret.first; return it->second; } //查找函數(shù) iterator find(const K& key) { return _t.Find(key); } private: RBTree<K, pair<const K, V>, MapKeyOfT> _t; }; void test_map1() { map<int, int> m; int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; for (auto e : a) { m.insert(make_pair(e, e)); } map<int, int>::iterator it = m.begin(); while (it != m.end()) { //it->first += 100; it->second += 100; cout << it->first << ":" << it->second << endl; ++it; } cout << endl; } }
以上就是C++用一棵紅黑樹同時封裝出set與map的實現(xiàn)詳解的詳細內(nèi)容,更多關于C++紅黑樹封裝set與map的資料請關注腳本之家其它相關文章!
- C++使用一棵紅黑樹同時封裝出map和set實例代碼
- C++ map與set封裝實現(xiàn)過程講解
- C++中map和set封裝實現(xiàn)示例
- C++深入探究哈希表如何封裝出unordered_set和unordered_map
- c++容器list、vector、map、set區(qū)別與用法詳解
- C++ STL入門教程(7) multimap、multiset的使用
- C++中map和set的簡介及使用詳解
- C++中set/multiset與map/multimap的使用詳解
- C++中map和set的使用詳細攻略
- C++中常見容器類的使用方法詳解(vector/deque/map/set)
- C++實現(xiàn)map和set封裝詳解
相關文章
C++ 動態(tài)內(nèi)存分配詳解(new/new[]和delete/delete[])
這篇文章主要介紹了C++ 動態(tài)內(nèi)存分配詳解(new/new[]和delete/delete[]),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-05-05Qt+Quick實現(xiàn)播放音樂和視頻的開發(fā)
這篇文章主要為大家詳細介紹了如何利用Qt+Quick實現(xiàn)播放音樂和視頻的開發(fā),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-03-03