【C++】:STL详解 —— 红黑树封装map和set
目录
红黑树的源代码
正向迭代器的代码
反向迭代器的代码
set的模拟实现
map的模拟实现
红黑树的源代码
#pragma once
#include <iostream>using namespace std;
// set ->key
// map ->key/value// set ->key
// map ->key/valueenum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};// 红黑树正向迭代器模板
// 模板参数:
// T - 结点数据类型
// Ref - 数据的引用类型
// Ptr - 数据的指针类型
template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef RBTreeNode<T> Node; // 红黑树结点类型typedef __TreeIterator<T, Ref, Ptr> Self; // 迭代器自身类型Node* _node; // 当前迭代器持有的结点指针// 构造函数// 参数:// node - 需要包装的结点指针__TreeIterator(Node* node): _node(node) // 初始化持有的结点指针{}// 解引用操作符(获取数据引用)Ref operator*(){return _node->_data; // 返回结点存储数据的引用}// 箭头操作符(获取数据指针)Ptr operator->(){return &_node->_data; // 返回指向结点数据的指针}// 不等比较操作符// 参数:// s - 要比较的迭代器// 返回值: 两个迭代器是否指向不同结点bool operator!=(const Self& s) const{return _node != s._node; // 直接比较结点指针}// 相等比较操作符// 参数:// s - 要比较的迭代器// 返回值: 两个迭代器是否指向相同结点bool operator==(const Self& s) const{return _node == s._node; // 直接比较结点指针}// 前置自增操作符(中序遍历顺序的下一个结点)Self operator++(){if (_node->_right) // 当前结点存在右子树{// 寻找右子树的最左结点(右子树中的最小结点)Node* left = _node->_right;while (left->_left){left = left->_left;}_node = left;}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->_left) // 当前结点存在左子树{// 寻找左子树的最右结点(左子树中的最大结点)Node* right = _node->_left;while (right->_right){right = right->_right;}_node = right;}else // 当前结点没有左子树{// 向上寻找第一个不是左孩子的祖先结点Node* cur = _node;Node* parent = cur->_parent;// 沿着父指针向上查找,直到当前结点是父结点的右孩子while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent; // 最终定位到的父结点即为前驱}return *this;}
};//反向迭代器---迭代器适配器
template<class Iterator>
struct ReverseIterator
{typedef ReverseIterator<Iterator> Self; //反向迭代器的类型typedef typename Iterator::reference Ref; //结点指针的引用typedef typename Iterator::pointer Ptr; //结点指针Iterator _it; //反向迭代器所封装的正向迭代器//构造函数ReverseIterator(Iterator it):_it(it) //根据所给正向迭代器构造一个反向迭代器{}Ref operator*(){return *_it; //通过调用正向迭代器的operator*返回结点数据的引用}Ptr operator->(){return _it.operator->(); //通过调用正向迭代器的operator->返回结点数据的指针}//前置++Self& operator++(){--_it; //调用正向迭代器的前置--return *this;}//前置--Self& operator--(){++_it; //调用正向迭代器的前置++return *this;}bool operator!=(const Self& s) const{return _it != s._it; //调用正向迭代器的operator!=}bool operator==(const Self& s) const{return _it == s._it; //调用正向迭代器的operator==}
};// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<const K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator; //正向迭代器typedef __TreeIterator<T, const T&, const T*> const_iterator;typedef ReverseIterator<iterator> reverse_iterator; //反向迭代器typedef ReverseIterator<const_iterator> const_reverse_iterator; iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}reverse_iterator rbegin(){//寻找最右结点Node* right = _root;while (right && right->_right){right = right->_right;}//返回最右结点的反向迭代器return reverse_iterator(iterator(right));}reverse_iterator rend(){//返回由nullptr构造得到的反向迭代器(不严谨)return reverse_iterator(iterator(nullptr));}const_reverse_iterator rbegin() const{//寻找最右结点Node* right = _root;while (right && right->_right){right = right->_right;}//返回最右结点的反向迭代器return const_reverse_iterator(const_iterator(right));}const_reverse_iterator rend() const{//返回由nullptr构造得到的反向迭代器(不严谨)return const_reverse_iterator(const_iterator(nullptr));}//构造函数RBTree():_root(nullptr){}//拷贝构造RBTree(const RBTree<K, T, KeyOfT>& t){_root = _Copy(t._root, nullptr);}//赋值运算符重载(现代写法)RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t){swap(_root, t._root);return *this; //支持连续赋值}//析构函数~RBTree(){_Destroy(_root);_root = nullptr;}size_t Size(){return _Size(_root);}bool Empty() const {return _root == nullptr;}void Clear() {_Destroy(_root);_root = nullptr;}void Swap(Node& other) {std::swap(_root, other._root);}//pair<iterator, bool> Insert(const T& data)pair<Node*, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* parent = nullptr;Node* cur = _root;KeyOfT kot;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 make_pair(cur, false);}}// 新增节点给红色cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){// g// p u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上更新处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_left){// 单旋// g// p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 双旋// g// p// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else // parent == grandfather->_right{// g// u p // c//Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p // c//RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(newnode, true);}//删除函数bool Erase(const K& key){KeyOfT kot;//用于遍历二叉树Node* parent = nullptr;Node* cur = _root;//用于标记实际的待删除结点及其父结点Node* delParentPos = nullptr;Node* delPos = nullptr;while (cur){if (key < kot(cur->_data)) //所给key值小于当前结点的key值{//往该结点的左子树走parent = cur;cur = cur->_left;}else if (key > kot(cur->_data)) //所给key值大于当前结点的key值{//往该结点的右子树走parent = cur;cur = cur->_right;}else //找到了待删除结点{if (cur->_left == nullptr) //待删除结点的左子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_right; //让根结点的右子树作为新的根结点if (_root){_root->_parent = nullptr;_root->_col = BLACK; //根结点为黑色}delete cur; //删除原根结点return true;}else{delParentPos = parent; //标记实际删除结点的父结点delPos = cur; //标记实际删除的结点}break; //进行红黑树的调整以及结点的实际删除}else if (cur->_right == nullptr) //待删除结点的右子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_left; //让根结点的左子树作为新的根结点if (_root){_root->_parent = nullptr;_root->_col = BLACK; //根结点为黑色}delete cur; //删除原根结点return true;}else{delParentPos = parent; //标记实际删除结点的父结点delPos = cur; //标记实际删除的结点}break; //进行红黑树的调整以及结点的实际删除}else //待删除结点的左右子树均不为空{//替换法删除//寻找待删除结点右子树当中key值最小的结点作为实际删除结点Node* minParent = cur;Node* minRight = cur->_right;while (minRight->_left){minParent = minRight;minRight = minRight->_left;}cur->_data = minRight->_data; //将待删除结点的_data改为minRight的_datadelParentPos = minParent; //标记实际删除结点的父结点delPos = minRight; //标记实际删除的结点break; //进行红黑树的调整以及结点的实际删除}}}if (delPos == nullptr) //delPos没有被修改过,说明没有找到待删除结点{return false;}//记录待删除结点及其父结点(用于后续实际删除)Node* del = delPos;Node* delP = delParentPos;//调整红黑树if (delPos->_col == BLACK) //删除的是黑色结点{if (delPos->_left) //待删除结点有一个红色的左孩子(不可能是黑色){delPos->_left->_col = BLACK; //将这个红色的左孩子变黑即可}else if (delPos->_right) //待删除结点有一个红色的右孩子(不可能是黑色){delPos->_right->_col = BLACK; //将这个红色的右孩子变黑即可}else //待删除结点的左右均为空{while (delPos != _root) //可能一直调整到根结点{if (delPos == delParentPos->_left) //待删除结点是其父结点的左孩子{Node* brother = delParentPos->_right; //兄弟结点是其父结点的右孩子//情况一:brother为红色if (brother->_col == RED){delParentPos->_col = RED;brother->_col = BLACK;RotateL(delParentPos);//需要继续处理brother = delParentPos->_right; //更新brother(否则在本循环中执行其他情况的代码会出错)}//情况二:brother为黑色,且其左右孩子都是黑色结点或为空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;}//需要继续处理delPos = delParentPos;delParentPos = delPos->_parent;}else{//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空if ((brother->_right == nullptr) || (brother->_right->_col == BLACK)){brother->_left->_col = BLACK;brother->_col = RED;RotateR(brother);//需要继续处理brother = delParentPos->_right; //更新brother(否则执行下面情况四的代码会出错)}//情况四:brother为黑色,且其右孩子是红色结点brother->_col = delParentPos->_col;delParentPos->_col = BLACK;brother->_right->_col = BLACK;RotateL(delParentPos);break; //情况四执行完毕后调整一定结束}}else //delPos == delParentPos->_right //待删除结点是其父结点的左孩子{Node* brother = delParentPos->_left; //兄弟结点是其父结点的左孩子//情况一:brother为红色if (brother->_col == RED) //brother为红色{delParentPos->_col = RED;brother->_col = BLACK;RotateR(delParentPos);//需要继续处理brother = delParentPos->_left; //更新brother(否则在本循环中执行其他情况的代码会出错)}//情况二:brother为黑色,且其左右孩子都是黑色结点或为空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;}//需要继续处理delPos = delParentPos;delParentPos = delPos->_parent;}else{//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空if ((brother->_left == nullptr) || (brother->_left->_col == BLACK)){brother->_right->_col = BLACK;brother->_col = RED;RotateL(brother);//需要继续处理brother = delParentPos->_left; //更新brother(否则执行下面情况四的代码会出错)}//情况四:brother为黑色,且其左孩子是红色结点brother->_col = delParentPos->_col;delParentPos->_col = BLACK;brother->_left->_col = BLACK;RotateR(delParentPos);break; //情况四执行完毕后调整一定结束}}}}}//进行实际删除if (del->_left == nullptr) //实际删除结点的左子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_right;if (del->_right)del->_right->_parent = delP;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_right;if (del->_right)del->_right->_parent = delP;}}else //实际删除结点的右子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_left;if (del->_left)del->_left->_parent = delP;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_left;if (del->_left)del->_left->_parent = delP;}}delete del; //实际删除结点return true;}//查找函数const_iterator Find(const K& key) const{KeyOfT kot;Node* cur = _root;while (cur){if (key < kot(cur->_data)) //key值小于该结点的值{cur = cur->_left; //在该结点的左子树当中查找}else if (key > kot(cur->_data)) //key值大于该结点的值{cur = cur->_right; //在该结点的右子树当中查找}else //找到了目标结点{return const_iterator(cur); //返回该结点}}return end(); //查找失败}void InOrder(){_InOrder(_root);cout << endl;}// 根节点->当前节点这条路径的黑色节点的数量bool Check(Node* root, int blacknum, const int refVal){if (root == nullptr){//cout << balcknum << endl;if (blacknum != refVal){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_col == BLACK){++blacknum;}return Check(root->_left, blacknum, refVal)&& Check(root->_right, blacknum, refVal);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;//参考值int refVal = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refVal;}cur = cur->_left;}int blacknum = 0;return Check(_root, blacknum, refVal);}int Height(){return _Height(_root);}private:size_t _Size(Node* root){if (root == NULL)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 _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}//拷贝树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;}//析构函数子函数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之间的联系parent->_right = subRL;if (subRL)subRL->_parent = parent;//建立parent与subR之间的联系subR->_left = parent;parent->_parent = subR;//建立subR与parentParent之间的联系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之间的联系parent->_left = subLR;if (subLR)subLR->_parent = parent;//建立parent与subL之间的联系subL->_right = parent;parent->_parent = subL;//建立subL与parentParent之间的联系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; //红黑树的根结点
};
正向迭代器的代码
// 红黑树正向迭代器模板
// 模板参数:
// T - 结点数据类型
// Ref - 数据的引用类型
// Ptr - 数据的指针类型
template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef Ref reference; // 数据引用类型定义typedef Ptr pointer; // 数据指针类型定义typedef RBTreeNode<T> Node; // 红黑树结点类型typedef __TreeIterator<T, Ref, Ptr> Self; // 迭代器自身类型Node* _node; // 当前迭代器持有的结点指针// 构造函数// 参数:// node - 需要包装的结点指针__TreeIterator(Node* node): _node(node) // 初始化持有的结点指针{}// 解引用操作符(获取数据引用)Ref operator*() {return _node->_data; // 返回结点存储数据的引用}// 箭头操作符(获取数据指针)Ptr operator->() {return &_node->_data; // 返回指向结点数据的指针}// 不等比较操作符// 参数:// s - 要比较的迭代器// 返回值: 两个迭代器是否指向不同结点bool operator!=(const Self& s) const {return _node != s._node; // 直接比较结点指针}// 相等比较操作符// 参数:// s - 要比较的迭代器// 返回值: 两个迭代器是否指向相同结点bool operator==(const Self& s) const {return _node == s._node; // 直接比较结点指针}// 前置自增操作符(中序遍历顺序的下一个结点)Self operator++() {if (_node->_right) // 当前结点存在右子树{// 寻找右子树的最左结点(右子树中的最小结点)Node* left = _node->_right;while (left->_left) {left = left->_left;}_node = left;}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->_left) // 当前结点存在左子树{// 寻找左子树的最右结点(左子树中的最大结点)Node* right = _node->_left;while (right->_right) {right = right->_right;}_node = right;}else // 当前结点没有左子树{// 向上寻找第一个不是左孩子的祖先结点Node* cur = _node;Node* parent = cur->_parent;// 沿着父指针向上查找,直到当前结点是父结点的右孩子while (parent && cur == parent->_left) {cur = parent;parent = parent->_parent;}_node = parent; // 最终定位到的父结点即为前驱}return *this;}
};
反向迭代器的代码
//反向迭代器---迭代器适配器
template<class Iterator>
struct ReverseIterator
{typedef ReverseIterator<Iterator> Self; //反向迭代器的类型typedef typename Iterator::reference Ref; //结点指针的引用typedef typename Iterator::pointer Ptr; //结点指针Iterator _it; //反向迭代器所封装的正向迭代器//构造函数ReverseIterator(Iterator it):_it(it) //根据所给正向迭代器构造一个反向迭代器{}Ref operator*(){return *_it; //通过调用正向迭代器的operator*返回结点数据的引用}Ptr operator->(){return _it.operator->(); //通过调用正向迭代器的operator->返回结点数据的指针}//前置++Self& operator++(){--_it; //调用正向迭代器的前置--return *this;}//前置--Self& operator--(){++_it; //调用正向迭代器的前置++return *this;}bool operator!=(const Self& s) const{return _it != s._it; //调用正向迭代器的operator!=}bool operator==(const Self& s) const{return _it == s._it; //调用正向迭代器的operator==}
};
set的模拟实现
| 成员函数 | 功能 |
|---|---|
| insert | 插入指定元素 |
| erase | 删除指定元素 |
| find | 查找指定元素 |
| size | 获取容器中元素的个数 |
| empty | 判断容器是否为空 |
| clear | 清空容器 |
| swap | 交换两个容器中的数据 |
| count | 获取容器中指定元素值的元素个数 |
#pragma once
#include"RBTree.h"namespace wh
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator; //正向迭代器typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;typedef typename RBTree<K, K, SetKeyOfT>::reverse_iterator reverse_iterator; //反向迭代器typedef typename RBTree<K, K, SetKeyOfT>::const_reverse_iterator const_reverse_iterator; //反向迭代器iterator begin() {return _t.begin();}iterator end(){return _t.end();}const_iterator begin() const{return _t.begin();}const_iterator end() const{return _t.end();}// 获取反向起始迭代器(指向最后一个元素)reverse_iterator rbegin(){return _t.rbegin();}// 获取反向结束迭代器(指向起始哨兵节点)reverse_iterator rend(){return _t.rend();}// 获取反向起始迭代器(指向最后一个元素)const_reverse_iterator rbegin() const{return _t.rbegin();}// 获取反向结束迭代器(指向起始哨兵节点)const_reverse_iterator rend() const{return _t.rend();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}// 删除函数void erase(const K& key){_t.Erase(key);}// 查找函数const_iterator find(const K& key) const{return _t.Find(key);}// 获取容器中元素的个数size_t size(){return _t.Size();}// 获取容器中指定元素值的元素个数size_t count(const K& key) const{return _t.Find(key) != _t.end() ? 1 : 0;}// 判断容器是否为空bool empty() const{return _t.Empty();}// 清空容器void clear(){_t.Clear();}void swap(set& other){_t.Swap(other._t);}private:RBTree<K, K, SetKeyOfT> _t;};
}
map的模拟实现
| 接口分类 | 接口名称 | 作用 |
|---|---|---|
| 插入操作 | insert | 插入键值对,返回插入结果(迭代器 + 是否成功)。 |
emplace | 原地构造键值对,避免临时对象拷贝。 | |
operator[] | 通过键访问值(若键不存在,插入默认值并返回引用)。 | |
| 删除操作 | erase | 删除指定键或迭代器范围内的键值对。 |
clear | 清空所有键值对。 | |
| 查找与访问 | find | 查找键,返回迭代器(未找到返回 end())。 |
count | 返回键的数量(0 或 1)。 | |
contains (C++20) | 检查键是否存在,返回布尔值。 | |
at | 安全访问值(键不存在时抛出异常)。 | |
| 容量查询 | empty | 检查容器是否为空。 |
size | 返回键值对数量。 | |
| 迭代器 | begin / end | 获取正向迭代器(按键升序)。 |
rbegin / rend | 获取反向迭代器(按键降序)。 |
#pragma once
#include"RBTree.h"namespace wh
{template<class K, class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};// 对类模板取内嵌类型,加typename告诉编译器这里是类型typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::reverse_iterator reverse_iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_reverse_iterator const_reverse_iterator;// 获取起始迭代器(指向第一个元素)iterator begin(){return _t.begin();}// 获取结束迭代器(指向末尾哨兵节点)iterator end(){return _t.end();}// 获取起始迭代器(指向第一个元素)const_iterator begin() const{return _t.begin();}// 获取结束迭代器(指向末尾哨兵节点)const_iterator end() const{return _t.end();}// 获取反向起始迭代器(指向最后一个元素)reverse_iterator rbegin(){return _t.rbegin();}// 获取反向结束迭代器(指向起始哨兵节点)reverse_iterator rend(){return _t.rend();}// 获取反向起始迭代器(指向最后一个元素)const_reverse_iterator rbegin() const{return _t.rbegin();}// 获取反向结束迭代器(指向起始哨兵节点)const_reverse_iterator rend() const{return _t.rend();}// 插入键值对// 参数: kv - 要插入的键值对// 返回值: 包含迭代器和插入结果的 pairpair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}// 下标访问运算符重载// 参数: key - 要访问的键// 返回值: 对应值的引用(若键不存在则自动插入默认值)V& operator[](const K& key){// 尝试插入键值对(若存在则获取已存在的元素)pair<iterator, bool> ret = insert(make_pair(key, V()));iterator it = ret.first; // 获取迭代器return it->second; // 返回值的引用}// 删除指定键的元素// 参数: key - 要删除的键void erase(const K& key){_t.Erase(key);}// 查找指定键的元素// 参数: key - 要查找的键// 返回值: 指向元素的迭代器(若未找到则返回 end())iterator find(const K& key){return _t.Find(key);}// 获取容器中元素的个数size_t size(){return _t.Size();}// 获取容器中指定元素值的元素个数size_t count(const K& key) const{return _t.Find(key) != _t.end() ? 1 : 0;}// 判断容器是否为空bool empty() const{return _t.Empty();}// 清空容器void clear(){_t.Clear();}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}
相关文章:
【C++】:STL详解 —— 红黑树封装map和set
目录 红黑树的源代码 正向迭代器的代码 反向迭代器的代码 set的模拟实现 map的模拟实现 红黑树的源代码 #pragma once #include <iostream>using namespace std; // set ->key // map ->key/value// set ->key // map ->key/valueenum Colour {RED,BLAC…...
【QWEN】机器人控制器的控制周期越短精度越高吗
好的,用户问的是“机器人控制器的控制周期越短精度越高吗”。我需要根据提供的知识库内容来回答这个问题。首先,我得回忆一下知识库里的相关信息。 知识库里有几篇相关的文章。比如,[3]提到控制周期越短,控制性能的上限越高&…...
使用数据库和缓存的时候,是如何解决数据不一致的问题的?
1.缓存更新策略 1.1. 缓存旁路模式(Cache Aside) 在应用里负责管理缓存,读取时先查缓存,如果命中了则返回缓存,如果未命中就查询数据库,然后返回缓存,返回缓存的同时把数据给写入缓存中。更新…...
【c++】平移字符串
说明 实现字符串的左移与右移 示例代码 #include <iostream> #include <string> using namespace std;int main() {string str1 "12345";//左移2位string str2 str1.substr(2) str1.substr(0, 2);cout << str2 << endl;//右移2位&…...
基于Spring Boot的多级缓存架构实现
基于Spring Boot的多级缓存架构实现 以下是一个基于Spring Boot的多级缓存架构实现示例 多级缓存架构实现方案 1. 依赖配置(pom.xml) <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-star…...
为什么DDPG需要目标网络而A2C不需要?
在强化学习中,DDPG需要目标网络而A2C不需要的主要原因在于算法架构、更新方式和目标稳定性需求的差异: Q值估计的稳定性需求不同 DDPG的Critic网络需要估计状态-动作值函数 Q ( s , a ) Q(s,a) Q(s,a),其目标值的计算涉及下一个状态的最大Q值…...
蓝桥杯 C++ b组 统计子矩阵深度解析
题目大意:给定一个 NM 的矩阵 A,请你统计有多少个子矩阵 (最小11,最大NM) 满足子矩阵中所有数的和不超过给定的整数 K? 前言:这题很容易想到二维前缀和优化,然后枚举子矩阵,但这样时间复杂度为…...
YOLOv12本地部署教程——42%速度提升,让高效目标检测触手可及
YOLOv12 是“你只看一次”(You Only Look Once, YOLO)系列的最新版本,于 2025 年 2 月发布。它引入了注意力机制,提升了检测精度,同时保持了高效的实时性能。在保持速度的同时,显著提升了检测精度。例如&am…...
每天五分钟深度学习PyTorch:向更深的卷积神经网络挑战的ResNet
本文重点 ResNet大名鼎鼎,它是由何恺明团队设计的,它获取了2015年ImageNet冠军,它很好的解决了当神经网络层数过多出现的难以训练的问题,它创造性的设计了跳跃连接的方式,使得卷积神经网络的层数出现了大幅度提升,设置可以达到上千层,可以说resnet对于网络模型的设计具…...
C++11新特性 11.基于范围的for循环
一.简介 基本概念: 在 C 中,基于范围的 for 循环(Range-based for loop)是一种简化容器遍历的语法糖,适用于所有支持 begin() 和 end() 的容器(如 vector、map、array 等)。以下是其核心用法和…...
Linux搜索---locate
locate locate 是 Linux 系统中用于快速查找文件和目录的命令。它并非实时遍历文件系统,而是通过搜索预先建立的文件数据库来定位文件。该数据库由 updatedb 程序定期(通常是每天)更新,收录了系统中所有文件的路径信息࿰…...
c语言笔记 一维数组与二维数组
1.一维数组和二维数组名加1代表什么意思,偏移多少单位? 方法:1就是以数组的元素类型的字节为单位去偏移。 先看结论再代码验证: 一维数组名+1表示加一个整型单位的偏移量,也可以这么理解1就是以数组的元…...
认识Event Loop【1】
前言 这应该是一个系列文章,因为我觉得Event Loop(事件循环)是一件很抽象也很重要的一个机制。eventloop这个知识点处于非常杂糅的位置,和很多其他知识,如运行时、浏览器、渲染流程、数据结构、线程等等,也…...
《Linux栈破坏了,如何还原》
【栈破坏导读】栈破坏有了解过吗?何为栈破坏,栈破坏了,程序会立刻引发崩溃,我们通过gdb去调试coredump,栈被破坏的栈帧是没法被恢复的,这也给我们调试程序带来很大的困难,那如何还原栈破坏的第一…...
环形链表问题的探究与代码实现
在数据结构与算法的学习中,环形链表是一个经典的问题。它不仅考察对链表这种数据结构的理解,还涉及到指针操作和逻辑推理。本文将结合代码和图文,深入分析如何判断链表中是否有环以及如何找到环的入口点。 目录 一、判断链表中是否有环 …...
【CSS3】筑基篇
目录 复合选择器后代选择器子选择器并集选择器交集选择器伪类选择器 CSS 三大特性继承性层叠性优先级 背景属性背景色背景图背景图平铺方式背景图位置背景图缩放背景图固定背景复合属性 显示模式显示模式块级元素行内元素行内块元素 转换显示模式 结构伪类选择器结构伪类选择器…...
React:类组件(上)
kerwin老师我来了 类组件的创建 class组件,js里的类命名首字符大写,类里面包括构造函数,方法 组件类要继承React.Component才有效 必须包含render方法 import React from react class App extends React.Component{render() {return <…...
开启mysql远程登录
目录 前言开启步骤 前言 为了安全考虑,mysql默认不允许远程登录,需要我们自己开启。当然在远程登录之前mysql的端口也要开放。下面是mysql开启远程登录的步骤。 开启步骤 本地登录mysql mysql -u root -p然后输入登录密码 给登录账号授权 GRANT AL…...
Eclipse 查看 JAVA SE 23 官方API 源代码
第一步:下载 JAVA SE 23 官方API 源代码 JavaSE23API源代码资源-CSDN文库 (或者到open jdk网站JDK Builds from Oracle:)下载https://download.java.net/java/GA/jdk23.0.2/6da2a6609d6e406f85c491fcb119101b/7/GPL/openjdk-23.0.2_windows-…...
Spring Cloud之注册中心之Nacos的使用
目录 Naacos 服务注册/服务发现 引⼊Spring Cloud Alibaba依赖 引入Nacos依赖 引入Load Balance依赖 配置Nacos地址 服务端调用 启动服务 Naacos Nacos是Spring Cloud Alibaba的组件, Spring Cloud Alibaba遵循Spring Cloud中定义的服务注册, 服务发现规范. 因此使⽤Na…...
字符串相乘——力扣
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2", num2 "3" …...
机试准备第13天
第一题是模拟出入栈游戏。 #include <stdio.h> #include <stack> #include <iostream> using namespace std; int main() {string str;while(getline(cin, str)){stack<char> stk;int j 0;//扫描出栈序列strfor(char i a;i<z;i){stk.push(i);//每…...
基于OpenCV的车牌识别系统(源码+论文+部署教程)
运行环境 基于OpenCV的车牌识别系统运行环境如下: • Python: ≥ 3.5 • OpenCV: ≥ 4.0 • IDE工具:Visual Studio Code(可自行选择) • 技术栈:Python OpenCV Tkinte 主要功能 基于OpenCV的车牌识别系统主要…...
MySQL:CRUD(增删查改)
目录 一、准备工作 二、Create 新增 1、语法 2、单行数据全列插入 3、单行数据指定列插入 4、多行数据指定列插入 5、多行数据全列插入 三、Retrieve 检索 1、语法 2、全列查询 3、指定列查询 4、查询字段为表达式 (1)常量表达式 &…...
德鲁伊连接池
德鲁伊连接池(Druid Connection Pool)是一个开源的Java数据库连接池项目,用于提高数据库连接的性能和可靠性。德鲁伊连接池通过复用数据库连接、定时验证连接的可用性、自动回收空闲连接等机制,有效减少了数据库连接的创建和销毁开…...
【git】【网络】【项目配置运行】HTTP 协议的微型简易 Web 服务器---tinyEasyMuduoWebServer
【git】【网络】【项目配置运行】HTTP 协议的微型简易 Web 服务器—tinyEasyMuduoWebServer csdn项目: 原文链接:https://blog.csdn.net/weixin_45178775/article/details/122257814 github链接:https://github.com/wyewyewye/tinyEasyMuduo…...
每周一个网络安全相关工具——MetaSpLoit
一、Metasploit简介 Metasploit(MSF)是一款开源渗透测试框架,集成了漏洞利用、Payload生成、后渗透模块等功能,支持多种操作系统和硬件平台。其模块化设计(如exploits、auxiliary、payloads等)使其成为全球…...
Python入门———条件、循环
目录 语句 顺序语句 条件语句 缩进和代码块 判断年份是否是闰年 空语句 pass 循环 while 循环 求5的阶乘: 求1!2!3!4!5! for循环 打印1-10 打印2,4,6,8&#x…...
InDraw6.2.3 | 甾体、核苷、黄酮类化合物实现简称命名
导语 当化学家对着屏幕输入"2-amino-1,9-dihydro-6H-purin-6-one"时,隔壁生物学家可能正在搜索"鸟嘌呤";这种命名差异如同"火星文"与"地球语"的碰撞。现在,鹰谷InDraw 6.2.3版带着53种多环化合物的…...
Linux中的TCP编程接口基本使用
TCP编程接口基本使用 本篇介绍 在UDP编程接口基本使用已经介绍过UDP编程相关的接口,本篇开始介绍TCP编程相关的接口。有了UDP编程的基础,理解TCP相关的接口会更加容易,下面将按照两个方向使用TCP编程接口: 基本使用TCP编程接口…...
