Red-black tree source code
Next, we will encapsulate the red-black tree of a KV model, and at the same time simulate and realize the map and set in the C++STL library, the used Red-black tree source code as follows:
//Enumeration defines the color of the node enum Colour { RED, BLACK }; //Red-black tree node definition template<class K, class V> struct RBTreeNode { //trident chain RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; //Stored key-value pairs pair<K, V> _kv; //node color int _col; //red/black //Constructor RBTreeNode(const pair<K, V>& kv) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _kv(kv) , _col(RED) {} }; //Implementation of red-black tree template<class K, class V> class RBTree { typedef RBTreeNode<K, V> Node; public: //Constructor RBTree() :_root(nullptr) {} //copy construction RBTree(const RBTree<K, V>& t) { _root = _Copy(t._root, nullptr); } //Assignment operator overloading (modern notation) RBTree<K, V>& operator=(RBTree<K, V> t) { swap(_root, t._root); return *this; } //destructor ~RBTree() { _Destroy(_root); _root = nullptr; } //lookup function Node* Find(const K& key) { Node* cur = _root; while (cur) { if (key < cur->_kv.first) //The key value is less than the value of the node { cur = cur->_left; //Search in the left subtree of the node } else if (key > cur->_kv.first) //The key value is greater than the value of the node { cur = cur->_right; //Search in the right subtree of the node } else //target node found { return cur; //return to this node } } return nullptr; //Find failed } //insert function pair<Node*, bool> Insert(const pair<K, V>& kv) { if (_root == nullptr) //If the red-black tree is an empty tree, insert the node directly as the root node { _root = new Node(kv); _root->_col = BLACK; //The root node must be black return make_pair(_root, true); //Inserted successfully } //1. According to the insertion method of the binary search tree, find the position to be inserted Node* cur = _root; Node* parent = nullptr; while (cur) { if (kv.first < cur->_kv.first) //The key value of the node to be inserted is less than the key value of the current node { //Go to the left subtree of the node parent = cur; cur = cur->_left; } else if (kv.first > cur->_kv.first) //The key value of the node to be inserted is greater than the key value of the current node { //Go to the right subtree of the node parent = cur; cur = cur->_right; } else //The key value of the node to be inserted is equal to the key value of the current node { return make_pair(cur, false); //insert failed } } //2. Insert the node to be inserted into the tree cur = new Node(kv); //Constructs a node from the given value Node* newnode = cur; //Record the newly inserted node (for subsequent return) if (kv.first < parent->_kv.first) //The key value of the new node is less than the key value of the parent { //Insert to the left of parent parent->_left = cur; cur->_parent = parent; } else //The key value of the new node is greater than the key value of the parent { //Insert to the right of parent parent->_right = cur; cur->_parent = parent; } //3. If the parent node of the inserted node is red, the red-black tree needs to be adjusted while (parent&&parent->_col == RED) { Node* grandfather = parent->_parent; //parent is red, its parent node must exist if (parent == grandfather->_left) //parent is grandfather's left child { Node* uncle = grandfather->_right; //uncle is the right child of grandfather if (uncle&&uncle->_col == RED) //Case 1: uncle exists and is red { //color adjustment parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //continue to process cur = grandfather; parent = cur->_parent; } else //Case 2+ Case 3: uncle does not exist + uncle exists and is black { if (cur == parent->_left) { RotateR(grandfather); //Right monorotation //color adjustment grandfather->_col = RED; parent->_col = BLACK; } else //cur == parent->_right { RotateLR(grandfather); //Left and right double rotation //color adjustment grandfather->_col = RED; cur->_col = BLACK; } break; //After the subtree is rotated, the root of the subtree becomes black, and there is no need to continue to process it } } else //parent is the right child of grandfather { Node* uncle = grandfather->_left; //uncle is grandfather's left child if (uncle&&uncle->_col == RED) //Case 1: uncle exists and is red { //color adjustment uncle->_col = parent->_col = BLACK; grandfather->_col = RED; //continue to process cur = grandfather; parent = cur->_parent; } else //Case 2+ Case 3: uncle does not exist + uncle exists and is black { if (cur == parent->_left) { RotateRL(grandfather); //right left double rotation //color adjustment cur->_col = BLACK; grandfather->_col = RED; } else //cur == parent->_right { RotateL(grandfather); //left monorotation //color adjustment grandfather->_col = RED; parent->_col = BLACK; } break; //After the subtree is rotated, the root of the subtree becomes black, and there is no need to continue to process it } } } _root->_col = BLACK; //The color of the root node is black (it may be changed to red by case 1 and needs to be changed back to black) return make_pair(newnode, true); //Inserted successfully } //delete function bool Erase(const K& key) { //for traversing a binary tree Node* parent = nullptr; Node* cur = _root; //Used to mark the actual node to be deleted and its parent node Node* delParentPos = nullptr; Node* delPos = nullptr; while (cur) { if (key < cur->_kv.first) //The given key value is less than the key value of the current node { //Go to the left subtree of the node parent = cur; cur = cur->_left; } else if (key > cur->_kv.first) //The given key value is greater than the key value of the current node { //Go to the right subtree of the node parent = cur; cur = cur->_right; } else //Found the node to be deleted { if (cur->_left == nullptr) //The left subtree of the node to be deleted is empty { if (cur == _root) //The node to be deleted is the root node { _root = _root->_right; //Let the right subtree of the root node be the new root node if (_root) { _root->_parent = nullptr; _root->_col = BLACK; //root node is black } delete cur; //Delete the original root node return true; } else { delParentPos = parent; //Mark the parent node of the node that was actually deleted delPos = cur; //mark the actual deleted node } break; //Perform red-black tree adjustments and actual deletion of nodes } else if (cur->_right == nullptr) //The right subtree of the node to be deleted is empty { if (cur == _root) //The node to be deleted is the root node { _root = _root->_left; //Let the left subtree of the root node be the new root node if (_root) { _root->_parent = nullptr; _root->_col = BLACK; //root node is black } delete cur; //Delete the original root node return true; } else { delParentPos = parent; //Mark the parent node of the node that was actually deleted delPos = cur; //mark the actual deleted node } break; //Perform red-black tree adjustments and actual deletion of nodes } else //The left and right subtrees of the node to be deleted are not empty { //delete by substitution //Find the node with the smallest key value in the right subtree of the node to be deleted as the actual deleted node Node* minParent = cur; Node* minRight = cur->_right; while (minRight->_left) { minParent = minRight; minRight = minRight->_left; } cur->_kv.first = minRight->_kv.first; //Change the key of the node to be deleted to the key of minRight cur->_kv.second = minRight->_kv.second; //Change the value of the node to be deleted to the value of minRight delParentPos = minParent; //Mark the parent node of the node that was actually deleted delPos = minRight; //mark the actual deleted node break; //Perform red-black tree adjustments and actual deletion of nodes } } } if (delPos == nullptr) //delPos has not been modified, indicating that no node to be deleted was found { return false; } //Record the node to be deleted and its parent node (for subsequent actual deletion) Node* del = delPos; Node* delP = delParentPos; //tuned red-black tree if (delPos->_col == BLACK) //Deleted is the black node { if (delPos->_left) //The node to be deleted has a red left child (it cannot be black) { delPos->_left->_col = BLACK; //Just turn the red left child black } else if (delPos->_right) //The node to be deleted has a red right child (it cannot be black) { delPos->_right->_col = BLACK; //Just turn the red right child black } else //The left and right sides of the node to be deleted are empty { while (delPos != _root) //may be adjusted all the way to the root node { if (delPos == delParentPos->_left) //The node to be deleted is the left child of its parent node { Node* brother = delParentPos->_right; //A sibling node is the right child of its parent node //Case 1: brother is red if (brother->_col == RED) { delParentPos->_col = RED; brother->_col = BLACK; RotateL(delParentPos); //need to continue processing brother = delParentPos->_right; //Update brother (otherwise there will be an error in executing other codes in this loop) } //Case 2: brother is black, and its left and right children are all black nodes or empty if (((brother->_left == nullptr) || (brother->_left->_col == BLACK)) && ((brother->_right == nullptr) || (brother->_right->_col == BLACK))) { brother->_col = RED; if (delParentPos->_col == RED) { delParentPos->_col = BLACK; break; } //need to continue processing delPos = delParentPos; delParentPos = delPos->_parent; } else { //Case 3: brother is black, and its left child is a red node, and its right child is a black node or empty if ((brother->_right == nullptr) || (brother->_right->_col == BLACK)) { brother->_left->_col = BLACK; brother->_col = RED; RotateR(brother); //need to continue processing brother = delParentPos->_right; //Update brother (otherwise an error will occur when executing the code in case 4 below) } //Situation 4: brother is black, and its right child is a red node brother->_col = delParentPos->_col; delParentPos->_col = BLACK; brother->_right->_col = BLACK; RotateL(delParentPos); break; //Situation 4: After the implementation is completed, the adjustment must be completed } } else //delPos == delParentPos->_right //The node to be deleted is the left child of its parent node { Node* brother = delParentPos->_left; //A sibling node is the left child of its parent node //Case 1: brother is red if (brother->_col == RED) //brother is red { delParentPos->_col = RED; brother->_col = BLACK; RotateR(delParentPos); //need to continue processing brother = delParentPos->_left; //Update brother (otherwise there will be an error in executing other codes in this loop) } //Case 2: brother is black, and its left and right children are all black nodes or empty if (((brother->_left == nullptr) || (brother->_left->_col == BLACK)) && ((brother->_right == nullptr) || (brother->_right->_col == BLACK))) { brother->_col = RED; if (delParentPos->_col == RED) { delParentPos->_col = BLACK; break; } //need to continue processing delPos = delParentPos; delParentPos = delPos->_parent; } else { //Case 3: brother is black, and its right child is a red node, and its left child is a black node or empty if ((brother->_left == nullptr) || (brother->_left->_col == BLACK)) { brother->_right->_col = BLACK; brother->_col = RED; RotateL(brother); //need to continue processing brother = delParentPos->_left; //Update brother (otherwise an error will occur when executing the code in case 4 below) } //Situation 4: brother is black, and its left child is a red node brother->_col = delParentPos->_col; delParentPos->_col = BLACK; brother->_left->_col = BLACK; RotateR(delParentPos); break; //Situation 4: After the implementation is completed, the adjustment must be completed } } } } } //do the actual deletion if (del->_left == nullptr) //The left subtree of the actually deleted node is empty { if (del == delP->_left) //The actual deleted node is the left child of its parent node { delP->_left = del->_right; if (del->_right) del->_right->_parent = delP; } else //The actual deleted node is the right child of its parent node { delP->_right = del->_right; if (del->_right) del->_right->_parent = delP; } } else //The right subtree of the actually deleted node is empty { if (del == delP->_left) //The actual deleted node is the left child of its parent node { delP->_left = del->_left; if (del->_left) del->_left->_parent = delP; } else //The actual deleted node is the right child of its parent node { delP->_right = del->_left; if (del->_left) del->_left->_parent = delP; } } delete del; //actually delete the node return true; } private: //copy tree 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; } //destructor subfunction void _Destroy(Node* root) { if (root == nullptr) { return; } _Destroy(root->_left); _Destroy(root->_right); delete root; } //left monorotation void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* parentParent = parent->_parent; //Establish a connection between subRL and parent parent->_right = subRL; if (subRL) subRL->_parent = parent; //Establish a connection between parent and subR subR->_left = parent; parent->_parent = subR; //Establish a connection between subR and parentParent if (parentParent == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } } //Right monorotation void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* parentParent = parent->_parent; //Establish a connection between subLR and parent parent->_left = subLR; if (subLR) subLR->_parent = parent; //Establish a connection between parent and subL subL->_right = parent; parent->_parent = subL; //Establish a connection between subL and parentParent if (parentParent == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subL; } else { parentParent->_right = subL; } subL->_parent = parentParent; } } //Left and right double rotation void RotateLR(Node* parent) { RotateL(parent->_left); RotateR(parent); } //right left double rotation void RotateRL(Node* parent) { RotateR(parent->_right); RotateL(parent); } Node* _root; //The root node of the red-black tree };
Red-black tree template parameter control
We all know that set is the container of K model, and map is the container of KV model, so how can we use a red-black tree of KV model to realize map and set at the same time?
Here we need to control the template parameters passed to the underlying red-black tree by map and set. In order to distinguish it from the template parameters of the original red-black tree, we change the name of the second template parameter of the red-black tree to T.
template<class K, class T> class RBTree
The T template parameter may only be the key-value Key, or may be a key-value pair composed of Key and Value. If it is a set container, then the template parameters it passes into the underlying red-black tree are Key and Key:
template<class K> class set { public: //... private: RBTree<K, K> _t; };
But if it is a map container, then the template parameter passed to the underlying red-black tree is Key and the key-value pair composed of Key and Value:
template<class K, class V> class map { public: //... private: RBTree<K, pair<K, V>> _t; };
Can you not use the first template parameter of the red-black tree, but only keep the second template parameter?
At first glance, it seems to be possible, because at this time the second template parameter of the red-black tree also has a key value Key, but in fact the first template parameter of the red-black tree cannot be omitted.
For the set container, it is of course no problem to omit the first parameter of the red-black tree, because the second parameter of the set passed to the red-black tree is the same as the first parameter. But it doesn't work for the map container, because some of the interfaces provided by the map container only require the key value Key, such as find and erase.
Data stored in red-black tree nodes
Now the template parameters of the red-black tree have become K and T, so what should be stored in the red-black tree nodes?
As mentioned earlier, due to the difference in the upper container, the K and T in the underlying red-black tree are also different:
- set container: K and T both represent the key value Key.
- map container: K represents the key-value Key, and T represents the key-value pair composed of Key and Value.
For the set container, the storage of K and T in the underlying red-black tree node is the same, but for the map container, the underlying red-black tree can only store T. Since the underlying red-black tree does not know whether the upper-level container is a map or a set, it is sufficient to directly store T in the nodes of the red-black tree.
In this way, when the upper-level container is a set, the key-value Key is stored in the node; when the upper-level container is a map, the key-value pair <Key, Value> is stored in the node.
The changed code is as follows:
//Red-black tree node definition template<class T> struct RBTreeNode { //trident chain RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; //stored data T _data; //node color int _col; //red/black //Constructor RBTreeNode(const T& data) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _col(RED) {} };
Addition of functors in template parameters
Now because T is stored in the node, this T may be a Key, or it may be a <Key, Value> key-value pair. So when we need to compare the key value of the node, how should we get the key value of the node?
When the upper-level container is a set, T is the key-value Key, which can be compared directly with T, but it will not work when the upper-level container is a map. At this time, we need to extract the key value from the <Key, Value> key-value pair After the Key, the Key value is used for comparison.
Therefore, the upper container map needs to provide a functor to the underlying red-black tree to obtain the key value Key in T. In this way, when the underlying red-black tree needs to compare the key values of two nodes, it can pass This functor is used to get the key value in T.
A functor is what makes a class look like a function. In fact, the implementation is to implement an operator() in the class, and this class has a function-like behavior, which is a functor class.
But for the underlying red-black tree, it does not know whether the upper container is a map or a set, so when it is necessary to compare the key values of two nodes, the underlying red-black tree will obtain the key value Key through the incoming functor , and then compare the key values of the two nodes.
Therefore, the set container also needs to pass in a functor to the underlying red-black tree. Although this functor seems useless alone, it is essential.
template<class K> class set { //functor struct SetKeyOfT { const K& operator()(const K& key) //return Key valueKey { return key; } }; public: //... private: RBTree<K, K, SetKeyOfT> _t; };
At this point, what the set container passes into the underlying red-black tree is the functor of set, and what the map container passes into the underlying red-black tree is the functor of map.
In this way, when the underlying red-black tree needs to compare the key value between two nodes, the key value of the corresponding node will be obtained through the incoming functor, and then compared, the following is the red-black tree Take the lookup function as an example:
//lookup function iterator Find(const K& key) { KeyOfT kot; Node* cur = _root; while (cur) { if (key < kot(cur->_data)) //The key value is less than the value of the node { cur = cur->_left; //Search in the left subtree of the node } else if (key > kot(cur->_data)) //The key value is greater than the value of the node { cur = cur->_right; //Search in the right subtree of the node } else //target node found { return iterator(cur); //return to this node } } return end(); //Find failed }
Note: In all places where the node key value is compared, it is necessary to obtain the key value of the corresponding node through the functor and then compare the key value.
Implementation of Forward Iterator
The forward iterator of the red-black tree actually encapsulates the node pointer, so there is actually only one member variable in the forward iterator, which is the pointer of the node encapsulated by the forward iterator.
//forward iterator template<class T, class Ref, class Ptr> struct __TreeIterator { typedef RBTreeNode<T> Node; //node type typedef __TreeIterator<T, Ref, Ptr> Self; //the type of the forward iterator Node* _node; //pointer to the node encapsulated by the forward iterator };
Therefore, we can construct a forward iterator through a node pointer.
//Constructor __TreeIterator(Node* node) :_node(node) //Constructs a forward iterator based on the given node pointer {}
When dereferencing the forward iterator, we simply return the reference to the corresponding node data.
Ref operator*() { return _node->_data; //Returns a reference to the node data }
When the -> operation is performed on the forward iterator, we can directly return the pointer to the corresponding node data.
Ptr operator->() { return &_node->_data; //Returns a pointer to the node data }
Of course, at least the == and != operators need to be overloaded in the forward iterator, and it is enough to directly judge whether the nodes encapsulated by the two iterators are the same during implementation.
//Determine whether two forward iterators are different bool operator!=(const Self& s) const { return _node != s._node; //Determine whether the nodes encapsulated by two forward iterators are the same } //Determine whether two forward iterators are the same bool operator==(const Self& s) const { return _node == s._node; //Determine whether the nodes encapsulated by two forward iterators are the same }
When the red-black tree forward iterator is implemented, the real difficulty is actually the overloading of the ++ and – operators.
When implementing the forward iterator of the red-black tree, after the forward iterator of a node performs ++ operation, it should find the next node of the current node according to the sequence of in-order traversal of the red-black tree.
The specific logic is as follows:
-
If the right subtree of the current node is not empty, the leftmost node in its right subtree should be found after the ++ operation.
-
If the right subtree of the current node is empty, after the ++ operation, the ancestor node whose child is not on the right of the father should be found in the ancestor node of the node.
//prefix ++ Self operator++() { if (_node->_right) //The right subtree of the node is not empty { //Find the leftmost node in the right subtree of the node Node* left = _node->_right; while (left->_left) { left = left->_left; } _node = left; //++ becomes the node } else //The right subtree of the node is empty { //Find ancestors whose children are not to the right of the father Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur == parent->_right) { cur = parent; parent = parent->_parent; } _node = parent; //++ becomes the node } return *this; }
After the forward iterator is implemented, we need to implement the iterator type in the implementation of the red-black tree typedef. It should be noted that in order to make the external typedef forward iterator type after iterator,we need to be in public Regional conduct typedef. Then implement member functions in the red-black tree begin and end: - begin The function returns the forward iterator of the first node in the inorder sequence, which is the leftmost node. - end The function returns the forward iterator of the next position of the last node in the inorder sequence. Here, a forward iterator is directly constructed with a null pointer. ```cpp template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //node type public: typedef __TreeIterator<T, T&, T*> iterator; //forward iterator iterator begin() { //Find the leftmost node Node* left = _root; while (left&&left->_left) { left = left->_left; } //Returns a forward iterator to the leftmost node return iterator(left); } iterator end() { //returns a forward iterator constructed from nullptr (not rigorous) return iterator(nullptr); } private: Node* _root; //The root node of the red-black tree };
In C++STL, the structure used by the underlying red-black tree to implement the forward iterator.
In fact, the iterators implemented above are flawed, because theoretically we perform – operation on the forward iterator at the end() position, and we should get the forward iterator of the last node, but we implement end( ), it directly returns the forward iterator constructed by nullptr, so the code implemented above cannot complete this operation.
Let's take a look at the implementation logic in the C++SLT library:
When the red-black tree is implemented in the C++STL library, a head node is added at the root node of the red-black tree. The left pointer of the head node points to the leftmost node in the red-black tree, and the right pointer points to the red-black tree. The rightmost node in the black tree, the parent pointer points to the root node of the red-black tree.
Under this structure, when implementing begin(), just use the left child of the head node to directly construct a forward iterator; when implementing rbegin(), directly use the right child of the head node to construct a reverse iterator (Actually, first use the node to construct a forward iterator, and then use the forward iterator to construct a reverse iterator), and when implementing end() and rend(), directly use the head node to construct the forward and A reverse iterator will do. Afterwards, through the control of the logic, it is possible to implement end() to perform the – operation to obtain the forward iterator of the last node.
However, implementing this structure requires changing the logic of many current functions. For example, when inserting a node, if it is inserted to the left of the leftmost node of the red-black tree, or to the right of the rightmost node, it is necessary to update the left and right pointers of the head node. , the actual implementation will not be carried out here.
Implementation of reverse iterator
The reverse iterator of the red-black tree is actually a package of the forward iterator, so the reverse iterator of the red-black tree is an iterator adapter.
There is only one member variable in the reverse iterator, which is the forward iterator encapsulated by the reverse iterator. The member functions of the reverse iterator complete the corresponding functions by calling the corresponding function of the forward iterator.
//reverse iterator --- iterator adapter template<class Iterator> struct ReverseIterator { typedef ReverseIterator<Iterator> Self; //type of reverse iterator typedef typename Iterator::reference Ref; //References to node pointers typedef typename Iterator::pointer Ptr; //node pointer Iterator _it; //Forward iterator wrapped by reverse iterator //Constructor ReverseIterator(Iterator it) :_it(it) //Constructs a reverse iterator from the given forward iterator {} Ref operator*() { return *_it; //Returns a reference to the node data by calling operator* on the forward iterator } Ptr operator->() { return _it.operator->(); //By calling the operator->pointer of the forward iterator to return the node data } //prefix ++ Self& operator++() { --_it; //Invoking the forward iterator's prefix -- return *this; } //Pre-- Self& operator--() { ++_it; //Call forward ++ on forward iterators return *this; } bool operator!=(const Self& s) const { return _it != s._it; //Call the forward iterator's operator!= } bool operator==(const Self& s) const { return _it == s._it; //Call operator== on the forward iterator } };
It should be noted that the reverse iterator only receives one template parameter, which is the type of the forward iterator, that is, the reverse iterator does not know the reference type of the node and the pointer type of the node, so we need to typedef these two types in the forward iterator, so that the reverse iterator can obtain the reference type of the node and the pointer type of the node through the forward iterator.
//forward iterator template<class T, class Ref, class Ptr> struct __TreeIterator { typedef Ref reference; //References to node pointers typedef Ptr pointer; //node pointer };
After the reverse iterator is implemented, we also need to typedef the iterator type in the implementation of the red-black tree, and implement the member functions rbegin and rend in the red-black tree:
-
The rbegin function returns the reverse iterator of the last node in the inorder sequence, that is, the rightmost node.
-
The rend function returns the reverse iterator of the position before the first node in the inorder sequence. Here, a null pointer is directly used to construct a reverse iterator.
template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //node type public: typedef ReverseIterator<iterator> reverse_iterator; //reverse iterator reverse_iterator rbegin() { //Find the rightmost node Node* right = _root; while (right&&right->_right) { right = right->_right; } //Returns a reverse iterator to the rightmost node return reverse_iterator(iterator(right)); } reverse_iterator rend() { //returns a reverse iterator constructed from nullptr (not strict) return reverse_iterator(iterator(nullptr)); } private: Node* _root; //The root node of the red-black tree };
<a name="dUany"></a> # The analog implementation of set After completing the above operations, set The simulation implementation of is very simple. The member functions in it all need to call the corresponding interface of the underlying red-black tree, but you need to pay attention to changing the node pointers in the return values of the insertion function and the search function into iterators. ```cpp template<class K> class set { //functor struct SetKeyOfT { const K& operator()(const K& key) //return Key valueKey { return key; } }; public: typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator; //forward iterator typedef typename RBTree<K, K, SetKeyOfT>::reverse_iterator 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(); } //insert function pair<iterator, bool> insert(const K& key) { return _t.Insert(key); } //delete function void erase(const K& key) { _t.Erase(key); } //lookup function iterator find(const K& key) { return _t.Find(key); } private: RBTree<K, K, SetKeyOfT> _t; };
Simulation implementation of map
The same is true for the analog implementation of map. Its member functions also call the corresponding interface of the underlying red-black tree. An overloaded function that implements the [] operator is required.
template<class K, class V> class map { //functor struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) //Returns the key-value Key in the key-value pair { return kv.first; } }; public: typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator; //forward iterator typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::reverse_iterator 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(); } //insert function pair<iterator, bool> insert(const pair<const K, V>& kv) { return _t.Insert(kv); } //[] operator overloading function V& operator[](const K& key) { pair<iterator, bool> ret = insert(make_pair(key, V())); iterator it = ret.first; return it->second; } //delete function void erase(const K& key) { _t.Erase(key); } //lookup function iterator find(const K& key) { return _t.Find(key); } private: RBTree<K, pair<K, V>, MapKeyOfT> _t; };
Code after packaging
Although the encapsulation process has been explained, there are still many details in the code change process. The complete encapsulation code is given below.
Red-black tree code
//Enumeration defines the color of the node enum Colour { RED, BLACK }; //Red-black tree node definition template<class T> struct RBTreeNode { //trident chain RBTreeNode<T>* _left; RBTreeNode<T>* _right; RBTreeNode<T>* _parent; //stored data T _data; //node color int _col; //red/black //Constructor RBTreeNode(const T& data) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _col(RED) {} }; //Implementation of red-black tree template<class K, class T, class KeyOfT> class RBTree { typedef RBTreeNode<T> Node; //node type public: typedef __TreeIterator<T, T&, T*> iterator; //forward iterator typedef ReverseIterator<iterator> reverse_iterator; //reverse iterator reverse_iterator rbegin() { //Find the rightmost node Node* right = _root; while (right&&right->_right) { right = right->_right; } //Returns a reverse iterator to the rightmost node return reverse_iterator(iterator(right)); } reverse_iterator rend() { //returns a reverse iterator constructed from nullptr (not strict) return reverse_iterator(iterator(nullptr)); } iterator begin() { //Find the leftmost node Node* left = _root; while (left&&left->_left) { left = left->_left; } //Returns a forward iterator to the leftmost node return iterator(left); } iterator end() { //returns a forward iterator constructed from nullptr (not rigorous) return iterator(nullptr); } //Constructor RBTree() :_root(nullptr) {} //copy construction RBTree(const RBTree<K, T, KeyOfT>& t) { _root = _Copy(t._root, nullptr); } //Assignment operator overloading (modern notation) RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t) { swap(_root, t._root); return *this; //Support for continuous assignment } //destructor ~RBTree() { _Destroy(_root); _root = nullptr; } //lookup function iterator Find(const K& key) { KeyOfT kot; Node* cur = _root; while (cur) { if (key < kot(cur->_data)) //The key value is less than the value of the node { cur = cur->_left; //Search in the left subtree of the node } else if (key > kot(cur->_data)) //The key value is greater than the value of the node { cur = cur->_right; //Search in the right subtree of the node } else //target node found { return iterator(cur); //return to this node } } return end(); //Find failed } //insert function pair<iterator, bool> Insert(const T& data) { if (_root == nullptr) //If the red-black tree is an empty tree, insert the node directly as the root node { _root = new Node(data); _root->_col = BLACK; //The root node must be black return make_pair(iterator(_root), true); //Inserted successfully } //1. According to the insertion method of the binary search tree, find the position to be inserted KeyOfT kot; Node* cur = _root; Node* parent = nullptr; while (cur) { if (kot(data) < kot(cur->_data)) //The key value of the node to be inserted is less than the key value of the current node { //Go to the left subtree of the node parent = cur; cur = cur->_left; } else if (kot(data) > kot(cur->_data)) //The key value of the node to be inserted is greater than the key value of the current node { //Go to the right subtree of the node parent = cur; cur = cur->_right; } else //The key value of the node to be inserted is equal to the key value of the current node { return make_pair(iterator(cur), false); //insert failed } } //2. Insert the node to be inserted into the tree cur = new Node(data); //Constructs a node from the given value Node* newnode = cur; //Record the newly inserted node (for subsequent return) if (kot(data) < kot(parent->_data)) //The key value of the new node is less than the key value of the parent { //Insert to the left of parent parent->_left = cur; cur->_parent = parent; } else //The key value of the new node is greater than the key value of the parent { //Insert to the right of parent parent->_right = cur; cur->_parent = parent; } //3. If the parent node of the inserted node is red, the red-black tree needs to be adjusted while (parent&&parent->_col == RED) { Node* grandfather = parent->_parent; //parent is red, its parent node must exist if (parent == grandfather->_left) //parent is grandfather's left child { Node* uncle = grandfather->_right; //uncle is the right child of grandfather if (uncle&&uncle->_col == RED) //Case 1: uncle exists and is red { //color adjustment parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //continue to process cur = grandfather; parent = cur->_parent; } else //Case 2+ Case 3: uncle does not exist + uncle exists and is black { if (cur == parent->_left) { RotateR(grandfather); //Right monorotation //color adjustment grandfather->_col = RED; parent->_col = BLACK; } else //cur == parent->_right { RotateLR(grandfather); //Left and right double rotation //color adjustment grandfather->_col = RED; cur->_col = BLACK; } break; //After the subtree is rotated, the root of the subtree becomes black, and there is no need to continue to process it } } else //parent is the right child of grandfather { Node* uncle = grandfather->_left; //uncle is grandfather's left child if (uncle&&uncle->_col == RED) //Case 1: uncle exists and is red { //color adjustment uncle->_col = parent->_col = BLACK; grandfather->_col = RED; //continue to process cur = grandfather; parent = cur->_parent; } else //Case 2+ Case 3: uncle does not exist + uncle exists and is black { if (cur == parent->_left) { RotateRL(grandfather); //right left double rotation //color adjustment cur->_col = BLACK; grandfather->_col = RED; } else //cur == parent->_right { RotateL(grandfather); //left monorotation //color adjustment grandfather->_col = RED; parent->_col = BLACK; } break; //After the subtree is rotated, the root of the subtree becomes black, and there is no need to continue to process it } } } _root->_col = BLACK; //The color of the root node is black (it may be changed to red by case 1 and needs to be changed back to black) return make_pair(iterator(newnode), true); //Inserted successfully } //delete function bool Erase(const K& key) { KeyOfT kot; //for traversing a binary tree Node* parent = nullptr; Node* cur = _root; //Used to mark the actual node to be deleted and its parent node Node* delParentPos = nullptr; Node* delPos = nullptr; while (cur) { if (key < kot(cur->_data)) //The given key value is less than the key value of the current node { //Go to the left subtree of the node parent = cur; cur = cur->_left; } else if (key > kot(cur->_data)) //The given key value is greater than the key value of the current node { //Go to the right subtree of the node parent = cur; cur = cur->_right; } else //Found the node to be deleted { if (cur->_left == nullptr) //The left subtree of the node to be deleted is empty { if (cur == _root) //The node to be deleted is the root node { _root = _root->_right; //Let the right subtree of the root node be the new root node if (_root) { _root->_parent = nullptr; _root->_col = BLACK; //root node is black } delete cur; //Delete the original root node return true; } else { delParentPos = parent; //Mark the parent node of the node that was actually deleted delPos = cur; //mark the actual deleted node } break; //Perform red-black tree adjustments and actual deletion of nodes } else if (cur->_right == nullptr) //The right subtree of the node to be deleted is empty { if (cur == _root) //The node to be deleted is the root node { _root = _root->_left; //Let the left subtree of the root node be the new root node if (_root) { _root->_parent = nullptr; _root->_col = BLACK; //root node is black } delete cur; //Delete the original root node return true; } else { delParentPos = parent; //Mark the parent node of the node that was actually deleted delPos = cur; //mark the actual deleted node } break; //Perform red-black tree adjustments and actual deletion of nodes } else //The left and right subtrees of the node to be deleted are not empty { //delete by substitution //Find the node with the smallest key value in the right subtree of the node to be deleted as the actual deleted node Node* minParent = cur; Node* minRight = cur->_right; while (minRight->_left) { minParent = minRight; minRight = minRight->_left; } cur->_data = minRight->_data; //Change the _data of the node to be deleted to the _data of minRight delParentPos = minParent; //Mark the parent node of the node that was actually deleted delPos = minRight; //mark the actual deleted node break; //Perform red-black tree adjustments and actual deletion of nodes } } } if (delPos == nullptr) //delPos has not been modified, indicating that no node to be deleted was found { return false; } //Record the node to be deleted and its parent node (for subsequent actual deletion) Node* del = delPos; Node* delP = delParentPos; //tuned red-black tree if (delPos->_col == BLACK) //Deleted is the black node { if (delPos->_left) //The node to be deleted has a red left child (it cannot be black) { delPos->_left->_col = BLACK; //Just turn the red left child black } else if (delPos->_right) //The node to be deleted has a red right child (it cannot be black) { delPos->_right->_col = BLACK; //Just turn the red right child black } else //The left and right sides of the node to be deleted are empty { while (delPos != _root) //may be adjusted all the way to the root node { if (delPos == delParentPos->_left) //The node to be deleted is the left child of its parent node { Node* brother = delParentPos->_right; //A sibling node is the right child of its parent node //Case 1: brother is red if (brother->_col == RED) { delParentPos->_col = RED; brother->_col = BLACK; RotateL(delParentPos); //need to continue processing brother = delParentPos->_right; //Update brother (otherwise there will be an error in executing other codes in this loop) } //Case 2: brother is black, and its left and right children are all black nodes or empty if (((brother->_left == nullptr) || (brother->_left->_col == BLACK)) && ((brother->_right == nullptr) || (brother->_right->_col == BLACK))) { brother->_col = RED; if (delParentPos->_col == RED) { delParentPos->_col = BLACK; break; } //need to continue processing delPos = delParentPos; delParentPos = delPos->_parent; } else { //Case 3: brother is black, and its left child is a red node, and its right child is a black node or empty if ((brother->_right == nullptr) || (brother->_right->_col == BLACK)) { brother->_left->_col = BLACK; brother->_col = RED; RotateR(brother); //need to continue processing brother = delParentPos->_right; //Update brother (otherwise an error will occur when executing the code in case 4 below) } //Situation 4: brother is black, and its right child is a red node brother->_col = delParentPos->_col; delParentPos->_col = BLACK; brother->_right->_col = BLACK; RotateL(delParentPos); break; //Situation 4: After the implementation is completed, the adjustment must be completed } } else //delPos == delParentPos->_right //The node to be deleted is the left child of its parent node { Node* brother = delParentPos->_left; //A sibling node is the left child of its parent node //Case 1: brother is red if (brother->_col == RED) //brother is red { delParentPos->_col = RED; brother->_col = BLACK; RotateR(delParentPos); //need to continue processing brother = delParentPos->_left; //Update brother (otherwise there will be an error in executing other codes in this loop) } //Case 2: brother is black, and its left and right children are all black nodes or empty if (((brother->_left == nullptr) || (brother->_left->_col == BLACK)) && ((brother->_right == nullptr) || (brother->_right->_col == BLACK))) { brother->_col = RED; if (delParentPos->_col == RED) { delParentPos->_col = BLACK; break; } //need to continue processing delPos = delParentPos; delParentPos = delPos->_parent; } else { //Case 3: brother is black, and its right child is a red node, and its left child is a black node or empty if ((brother->_left == nullptr) || (brother->_left->_col == BLACK)) { brother->_right->_col = BLACK; brother->_col = RED; RotateL(brother); //need to continue processing brother = delParentPos->_left; //Update brother (otherwise an error will occur when executing the code in case 4 below) } //Situation 4: brother is black, and its left child is a red node brother->_col = delParentPos->_col; delParentPos->_col = BLACK; brother->_left->_col = BLACK; RotateR(delParentPos); break; //Situation 4: After the implementation is completed, the adjustment must be completed } } } } } //do the actual deletion if (del->_left == nullptr) //The left subtree of the actually deleted node is empty { if (del == delP->_left) //The actual deleted node is the left child of its parent node { delP->_left = del->_right; if (del->_right) del->_right->_parent = delP; } else //The actual deleted node is the right child of its parent node { delP->_right = del->_right; if (del->_right) del->_right->_parent = delP; } } else //The right subtree of the actually deleted node is empty { if (del == delP->_left) //The actual deleted node is the left child of its parent node { delP->_left = del->_left; if (del->_left) del->_left->_parent = delP; } else //The actual deleted node is the right child of its parent node { delP->_right = del->_left; if (del->_left) del->_left->_parent = delP; } } delete del; //actually delete the node return true; } private: //copy tree 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; } //destructor subfunction void _Destroy(Node* root) { if (root == nullptr) { return; } _Destroy(root->_left); _Destroy(root->_right); delete root; } //left monorotation void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* parentParent = parent->_parent; //Establish a connection between subRL and parent parent->_right = subRL; if (subRL) subRL->_parent = parent; //Establish a connection between parent and subR subR->_left = parent; parent->_parent = subR; //Establish a connection between subR and parentParent if (parentParent == nullptr) { _root = subR; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subR; } else { parentParent->_right = subR; } subR->_parent = parentParent; } } //Right monorotation void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* parentParent = parent->_parent; //Establish a connection between subLR and parent parent->_left = subLR; if (subLR) subLR->_parent = parent; //Establish a connection between parent and subL subL->_right = parent; parent->_parent = subL; //Establish a connection between subL and parentParent if (parentParent == nullptr) { _root = subL; _root->_parent = nullptr; } else { if (parent == parentParent->_left) { parentParent->_left = subL; } else { parentParent->_right = subL; } subL->_parent = parentParent; } } //Left and right double rotation void RotateLR(Node* parent) { RotateL(parent->_left); RotateR(parent); } //right left double rotation void RotateRL(Node* parent) { RotateR(parent->_right); RotateL(parent); } Node* _root; //The root node of the red-black tree };
Forward iterator code
//forward iterator template<class T, class Ref, class Ptr> struct __TreeIterator { typedef Ref reference; //References to node pointers typedef Ptr pointer; //node pointer typedef RBTreeNode<T> Node; //node type typedef __TreeIterator<T, Ref, Ptr> Self; //the type of the forward iterator Node* _node; //pointer to the node encapsulated by the forward iterator //Constructor __TreeIterator(Node* node) :_node(node) //Constructs a forward iterator based on the given node pointer {} Ref operator*() { return _node->_data; //Returns a reference to the node data } Ptr operator->() { return &_node->_data; //Returns a pointer to the node data } //Determine whether two forward iterators are different bool operator!=(const Self& s) const { return _node != s._node; //Determine whether the nodes encapsulated by two forward iterators are the same } //Determine whether two forward iterators are the same bool operator==(const Self& s) const { return _node == s._node; //Determine whether the nodes encapsulated by two forward iterators are the same } //prefix ++ Self operator++() { if (_node->_right) //The right subtree of the node is not empty { //Find the leftmost node in the right subtree of the node Node* left = _node->_right; while (left->_left) { left = left->_left; } _node = left; //++ becomes the node } else //The right subtree of the node is empty { //Find ancestors whose children are not to the right of the father Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur == parent->_right) { cur = parent; parent = parent->_parent; } _node = parent; //++ becomes the node } return *this; } //Pre-- Self operator--() { if (_node->_left) //The left subtree of the node is not empty { //Find the rightmost node in the left subtree of the node Node* right = _node->_left; while (right->_right) { right = right->_right; } _node = right; //-- then become the node } else //The left subtree of the node is empty { //Find ancestors whose children are not to the father's left Node* cur = _node; Node* parent = cur->_parent; while (parent&&cur == parent->_left) { cur = parent; parent = parent->_parent; } _node = parent; //-- then become the node } return *this; } };
code for reverse iterator
//reverse iterator --- iterator adapter template<class Iterator> struct ReverseIterator { typedef ReverseIterator<Iterator> Self; //type of reverse iterator typedef typename Iterator::reference Ref; //References to node pointers typedef typename Iterator::pointer Ptr; //node pointer Iterator _it; //Forward iterator wrapped by reverse iterator //Constructor ReverseIterator(Iterator it) :_it(it) //Constructs a reverse iterator from the given forward iterator {} Ref operator*() { return *_it; //Returns a reference to the node data by calling operator* on the forward iterator } Ptr operator->() { return _it.operator->(); //By calling the operator->pointer of the forward iterator to return the node data } //prefix ++ Self& operator++() { --_it; //Invoking the forward iterator's prefix -- return *this; } //Pre-- Self& operator--() { ++_it; //Call forward ++ on forward iterators return *this; } bool operator!=(const Self& s) const { return _it != s._it; //Call the forward iterator's operator!= } bool operator==(const Self& s) const { return _it == s._it; //Call operator== on the forward iterator } };
set code
namespace cl //prevent naming conflicts { template<class K> class set { //functor struct SetKeyOfT { const K& operator()(const K& key) //return Key valueKey { return key; } }; public: typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator; //forward iterator typedef typename RBTree<K, K, SetKeyOfT>::reverse_iterator 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(); } //insert function pair<iterator, bool> insert(const K& key) { return _t.Insert(key); } //delete function void erase(const K& key) { _t.Erase(key); } //lookup function iterator find(const K& key) { return _t.Find(key); } private: RBTree<K, K, SetKeyOfT> _t; }; }
map code
namespace cl //prevent naming conflicts { template<class K, class V> class map { //functor struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) //Returns the key-value Key in the key-value pair { return kv.first; } }; public: typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator; //forward iterator typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::reverse_iterator 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(); } //insert function pair<iterator, bool> insert(const pair<const K, V>& kv) { return _t.Insert(kv); } //[] operator overloading function V& operator[](const K& key) { pair<iterator, bool> ret = insert(make_pair(key, V())); iterator it = ret.first; return it->second; } //delete function void erase(const K& key) { _t.Erase(key); } //lookup function iterator find(const K& key) { return _t.Find(key); } private: RBTree<K, pair<K, V>, MapKeyOfT> _t; }; }