当前位置: 首页 > news >正文

【C++】22. 红黑树封装实现Mymap和Myset

上一章节我们实现了红黑树,这一章节我们就用红黑树封装来实现一个我们自己的map和set

1. 源码及框架分析

SGI-STL 3.0版本的源代码中,map和set的实现主要分布在若干头文件中,这些头文件构成了这两个容器的完整实现架构:

  1. 核心头文件

    • map/stl_map.h:定义了map容器的完整实现
    • set/stl_set.h:定义了set容器的完整实现
    • stl_tree.h:提供红黑树基础实现,是map和set的底层数据结构
  2. 实现细节

    • 红黑树实现stl_tree.h中实现了红黑树(RB-Tree)数据结构,包括:

      • 节点结构定义(_Rb_tree_node
      • 迭代器实现(_Rb_tree_iterator
      • 基本操作(插入、删除、旋转等)
    • 容器适配

      • stl_map.h将红黑树适配为关联容器,提供key-value映射功能
      • stl_set.h将红黑树适配为集合容器,存储唯一键值
  3. 关键特性实现

    • 迭代器失效保证
    • 元素自动排序
    • 插入删除的O(log n)时间复杂度保证
    • 内存管理机制
  4. 依赖关系: 这些头文件还依赖于STL的其他基础组件,如:

    • stl_pair.h(用于存储key-value对)
    • stl_alloc.h(内存分配器)
    • stl_function.h(比较函数对象)
  5. 该实现充分体现了STL的设计理念,将数据结构与算法分离,通过模板实现泛型编程,同时保证了高效的运行性能。

map和set的核心实现框架可概括如下:

// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>
// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>
// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:// typedefs:typedef Key key_type;typedef Key value_type;
private:typedef rb_tree<key_type, value_type,identity<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing set
};
// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:// typedefs:typedef Key key_type;typedef T mapped_type;typedef pair<const Key, T> value_type;
private:typedef rb_tree<key_type, value_type,select1st<value_type>, key_compare, Alloc> rep_type;rep_type t; // red-black tree representing map
};
// stl_tree.h
struct __rb_tree_node_base
{typedef __rb_tree_color_type color_type;typedef __rb_tree_node_base* base_ptr;color_type color;base_ptr parent;base_ptr left;base_ptr right;
};
// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc= alloc>
class rb_tree {
protected:typedef void* void_pointer;typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef rb_tree_node* link_type;typedef Key key_type;typedef Value value_type;
public:// insert⽤的是第⼆个模板参数左形参pair<iterator, bool> insert_unique(const value_type& x);// erase和find⽤第⼀个模板参数做形参size_type erase(const key_type& x);iterator find(const key_type& x);
protected:size_type node_count; // keeps track of size of treelink_type header;
};
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{typedef __rb_tree_node<Value>* link_type;Value value_field;
};

1.1 容器共性

  • 底层数据结构
    set 和 map 均通过 rb_tree(红黑树)实现,确保操作(插入、删除、查找)的时间复杂度为 O(log n)

  • 成员变量
    二者内部均包含一个 rep_type t(即 rb_tree 实例),所有操作委托给红黑树完成。


1.2 关键差异:元素类型与键提取方式

set 的实现

template <class Key, class Compare, class Alloc>
class set {
private:typedef rb_tree<Key, Key, identity<Key>, Compare, Alloc> rep_type;rep_type t; // 红黑树存储 Key 本身
};
  • 元素类型value_type = Key(键与值相同)。

  • 键提取器identity<value_type>
    直接返回元素自身作为键(value → key)。

map 的实现

template <class Key, class T, class Compare, class Alloc>
class map {
private:typedef rb_tree<Key, pair<const Key, T>, select1st<pair<const Key, T>>, Compare, Alloc> rep_type;rep_type t; // 红黑树存储 pair<const Key, T>
};
  • 元素类型value_type = pair<const Key, T>(键值对,键不可修改)。

  • 键提取器select1st<value_type>
    从 pair 中提取第一个元素作为键(pair.first → key)。


1.3 红黑树(rb_tree)核心设计

节点结构

// 基类:管理树结构指针和颜色
struct __rb_tree_node_base {color_type color;      // 节点颜色(红/黑)base_ptr parent;       // 父节点base_ptr left;         // 左子节点base_ptr right;        // 右子节点
};// 派生类:存储实际数据
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base {Value value_field;     // 节点存储的值(set: Key, map: pair<const Key, T>)
};

树类模板

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
class rb_tree {
protected:link_type header;      // 哨兵节点(父节点指向根,左/右指向最小/最大节点)size_type node_count;  // 节点数量(O(1) 获取 size)public:// 核心操作pair<iterator, bool> insert_unique(const Value& x); // 插入唯一键size_type erase(const Key& x);                      // 按键删除iterator find(const Key& x);                        // 按键查找
};
  • 模板参数

    • Key:键类型(用于查找/删除)。

    • Value:节点实际存储类型(set 为 Keymap 为 pair<const Key, T>)。

    • KeyOfValue:从 Value 提取键的函数对象(set 用 identitymap 用 select1st)。

    • Compare:键比较函数(默认 less<Key>)。


1.4 操作逻辑

  • 插入
    insert_unique(const value_type& x)
    使用 KeyOfValue 提取键,通过 Compare 比较键值,在树中查找合适位置插入。若键已存在,返回失败。

  • 删除
    erase(const key_type& x)
    直接使用传入的键 x 在树中查找并删除节点。

  • 查找
    find(const key_type& x)
    按键值在树中进行二分查找。


1.5 设计优势

  1. 复用性
    通过模板参数 KeyOfValue 和 Value 的差异,rb_tree 同时支持 setmapmultisetmultimap

  2. 效率

    • 红黑树的自平衡特性保证操作复杂度为 O(log n)

    • header 哨兵节点优化了 begin()/end() 操作(直接访问最小/最大节点)。

    • node_count 成员使 size() 操作时间复杂度为 O(1)

  3. 类型安全
    map 的键被设计为 const Key,防止用户修改键导致树结构破坏。


红黑树模板参数设计分析

红黑树的泛型实现机制

通过源码分析可以看到,rb_tree采用了一个巧妙的泛型设计思想。其核心在于:

  • 通过第二个模板参数Value来控制红黑树节点_rb_tree_node中存储的数据类型
  • 这使得同一套红黑树实现可以同时支持两种不同的使用场景:
    • 纯键值搜索场景(如set
    • 键值对搜索场景(如map

set和map的不同实例化方式

具体实现上有以下关键区别:

  1. set的实现

    • 实例化rb_tree时第二个模板参数直接传入key
    • 例如:rb_tree<Key, Key>
    • 节点中存储的就是键值本身
  2. map的实现

    • 实例化rb_tree时第二个模板参数传入std::pair<const key, T>
    • 例如:rb_tree<Key, std::pair<const Key, Value>>
    • 节点中存储的是键值对

模板参数命名解析

源码中存在以下命名惯例需要注意:

  • T代表的是节点存储的实际数据类型(即Value
  • value_type在源码中表示红黑树节点存储的真实数据类型
    • 这与日常使用中的key/value概念不同
    • set中,value_type就是Key
    • map中,value_typestd::pair<const Key, T>

双模板参数的必要性

关于为何需要两个模板参数(KeyValue):

  1. Value参数的作用

    • 控制节点实际存储的数据类型
    • 决定红黑树是作为key容器还是key/value容器
  2. Key参数的作用

    • 作为find()erase()等操作的参数类型
    • 对于set
      • KeyValue相同
      • 如:find(const Key&)
    • 对于map
      • KeyValue不同
      • 如:find(const Key&)(虽然存储的是pair,但查找时只用Key)

源码命名风格问题

值得注意的命名不一致问题:

  • set模板参数使用Key命名
  • map模板参数使用KeyT命名
  • rb_tree模板参数又使用KeyValue命名
  • 这种不一致的命名风格反映了即使是经验丰富的开发者也可能存在编码规范不统一的问题

2. 模拟实现map和set

2.1 实现出复用红黑树的框架,并支持insert

框架设计思路

参考STL源码框架结构,我们将map和set的实现复用了之前已完成实现的红黑树(RBTree)数据结构。这种复用设计遵循了STL的设计理念,通过模板和仿函数实现代码复用,避免了重复造轮子。

模板参数调整

相比原始实现,我们对模板参数进行了以下优化调整:

  • 使用K表示键类型参数
  • 使用V表示值类型参数
  • 红黑树内部数据类型统一使用T表示模板参数

关键问题与解决方案

类型比较问题: 当RBTree实现为泛型模板后,编译器无法确定模板参数T具体是简单的键类型K(set的情况),还是键值对pair<K, V>(map的情况)。这导致在insert操作内部进行节点比较时产生歧义:

// 问题示例
if (cur->_data < new_node->_data)  // 当T是pair时,这会比较key和value

解决方案: 我们引入了提取键值的仿函数机制:

  1. 在map和set层分别实现:
    • MapKeyOfT:从pair<K,V>中提取key
    • SetKeyOfT:从K中直接返回key(保持原样)
  2. 将这些仿函数作为模板参数KeyOfT传给RBTree
  3. RBTree内部通过KeyOfT仿函数统一获取比较键值

具体实现示例

// map层的键提取仿函数
struct MapKeyOfT {const K& operator()(const pair<K, V>& kv) {return kv.first;}
};// set层的键提取仿函数
struct SetKeyOfT {const K& operator()(const K& key) {return key;}
};// RBTree中的比较逻辑调整
template<class T, class KeyOfT>
bool RBTree<T, KeyOfT>::Insert(const T& data) {KeyOfT kot;  // 实例化键提取仿函数// ...if (kot(cur->_data) < kot(data)) {  // 统一使用kot提取键值进行比较// 右子树处理}else if (kot(data) < kot(cur->_data)) {// 左子树处理}else {// 键值相等处理}// ...
}

应用场景说明

这种设计使得我们的RBTree可以:

  1. 作为set的底层容器时,直接比较键值
  2. 作为map的底层容器时,仅比较pair中的键部分
  3. 保持统一的插入接口,同时正确处理两种场景的键比较逻辑

通过这种仿函数和模板的组合设计,我们实现了map和set对红黑树代码的高度复用,同时保证了类型安全和比较逻辑的正确性。

具体代码如下:

// Mymap.h
#pragma once#include "RBTree.h"namespace Ro
{template<class K, class V>class Mymap{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:bool insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, V, MapKeyOfT> _t;};
}// Myset.h
#pragma once#include "RBTree.h"namespace Ro
{template<class K>class Myset{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:bool insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}// RBTree.h
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED)          // 新节点默认红色(符合红黑树插入规则){}T _data;          RBTreeNode<T>* _left; // 左子节点RBTreeNode<T>* _right;// 右子节点RBTreeNode<T>* _parent; // 父节点Colour _col;             // 节点颜色
};template<class K, class T, class KeyOfT>
class RBTree
{using Node = RBTreeNode<T>;
public:bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}KeyOfT kt;Node* parent = nullptr;Node* cur = _root;while (cur){if (kt(cur->_data) < kt(data)){parent = cur;cur = cur->_right;}else if (kt(cur->_data) > kt(data)){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(data);if (kt(parent->_data) < kt(data)){parent->_right = cur;}else{parent->_left = cur;}//链接父亲节点cur->_parent = parent;// 父节点为红色,需要颜色修正while (parent && parent->_col == RED){Node* grandfather = parent->_parent; // 祖父节点必存在(因父为红,不可能是根)// 分父节点是祖父的左/右孩子两种情况处理if (grandfather->_left == parent){//		g(B)//	p(R)	   u(分情况)Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED) // 情况1:变色处理{//		g(B)//	p(R)	   u(存在且为红色)//c(R)// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 情况2,3:旋转 + 变色{if (parent->_left == cur) // 右单旋 + 变色{//		g(B)//	p(R)	   u(不存在或为黑色)//c(R)RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else // 左右双旋 + 变色{//		g(B)//	p(R)	   u(不存在或为黑色)//    c(R)RotateL(parent);RotateR(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//此时旋转之后的子树根节点为parent或cur,但都变为黑色,更新到黑结束break;}}else{//		      g(B)//	u(分情况)		 p(R)Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED) // 情况1:变色处理{//			  g(B)//	u(存在且为红色)		p(R)//							c(R)// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 情况2,3:旋转 + 变色{if (parent->_right == cur) // 左单旋 + 变色{//			  g(B)//	u(不存在或为黑色)		p(R)//							c(R)RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else // 右左双旋 + 变色{//			  g(R)//	u(不存在或为黑色)		p(R)//					c(R)RotateR(parent);RotateL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//此时旋转之后的子树根节点为parent或cur,但都变为黑色,更新到黑结束break;}}}// 暴力处理:无论是否处理到根节点,都直接把根节点变为黑色(符合根节点为黑色规则)_root->_col = BLACK;return true;}// 右单旋void RotateR(Node* parent){Node* subL = parent->_left;// parent的左子节点(旋转后的新根)Node* subLR = subL->_right;// subL的右子节点(可能为空)// 旋转节点subL->_right = parent;parent->_left = subLR;// 维护父指针Node* pParent = parent->_parent;parent->_parent = subL;if (subLR) //若subLR存在,更新其父指针,避免堆空指针解引用{subLR->_parent = parent;}subL->_parent = pParent;// 维护parent的父节点if (parent == _root) // parent为根节点的情况{_root = subL;}else // parent是一棵局部子树的情况{if (pParent->_left == parent){pParent->_left = subL;}else{pParent->_right = subL;}}}// 左单旋void RotateL(Node* parent){Node* subR = parent->_right;// parent的右子节点(旋转后的新根)Node* subRL = subR->_left;// subR的左子节点(可能为空)// 旋转节点subR->_left = parent;parent->_right = subRL;// 维护父指针Node* pParent = parent->_parent;parent->_parent = subR;if (subRL) //若subRL存在,更新其父指针,避免堆空指针解引用{subRL->_parent = parent;}subR->_parent = pParent;// 维护parent的父节点if (parent == _root) // parent为根节点的情况{_root = subR;}else // parent是一棵局部子树的情况{if (pParent->_left == parent){pParent->_left = subR;}else{pParent->_right = subR;}}}Node* Find(const K& key){KeyOfT kt;Node* cur = _root;while (cur){if (kt(cur->_data) < key){cur = cur->_right;}else if (kt(cur->_data) > key){cur = cur->_left;}else{return cur;}}return nullptr;}int Height(){return _Height(_root);}int Size(){return _Size(_root);}
private:int _Size(Node* root){if (root == nullptr) return 0;int leftSize = _Size(root->_left);int rightSize = _Size(root->_right);return leftSize + rightSize + 1;}int _Height(Node* root){if (root == nullptr) return 0;int leftHigh = _Height(root->_left);int rightHigh = _Height(root->_right);return leftHigh > rightHigh ? leftHigh + 1 : rightHigh + 1;}private:Node* _root = nullptr;
};

2.2 支持iterator的实现

iterator核心源代码


struct __rb_tree_base_iterator
{typedef __rb_tree_node_base::base_ptr base_ptr;base_ptr node;void increment(){if (node->right != 0) {node = node->right;while (node->left != 0)node = node->left;}else {base_ptr y = node->parent;while (node == y->right) {node = y;y = y->parent;}if (node->right != y)node = y;}}void decrement(){if (node->color == __rb_tree_red &&node->parent->parent == node)node = node->right;else if (node->left != 0) {base_ptr y = node->left;while (y->right != 0)y = y->right;node = y;}else {base_ptr y = node->parent;while (node == y->left) {node = y;y = y->parent;}node = y;}}
};
template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{typedef Value value_type;typedef Ref reference;typedef Ptr pointer;typedef __rb_tree_iterator<Value, Value&, Value*> iterator;__rb_tree_iterator() {}__rb_tree_iterator(link_type x) { node = x; }__rb_tree_iterator(const iterator& it) { node = it.node; }reference operator*() const { return link_type(node)->value_field; }
#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */self& operator++() { increment(); return *this; }self& operator--() { decrement(); return *this; }inline bool operator==(const __rb_tree_base_iterator& x,const __rb_tree_base_iterator& y) {return x.node == y.node;}inline bool operator!=(const __rb_tree_base_iterator& x,const __rb_tree_base_iterator& y) {return x.node != y.node;}
};

红黑树迭代器源码分析(SGI STL v3.0)

1. 迭代器继承结构
struct __rb_tree_base_iterator { /* 基础迭代器 */ };
template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator { /* 具体迭代器 */ };
  • 基础迭代器:处理树节点指针移动的核心逻辑

  • 具体迭代器:添加类型定义和运算符重载


2. 关键成员变量
// 基础迭代器中
base_ptr node;  // __rb_tree_node_base* 类型
  • 核心指针:指向当前红黑树节点

  • 节点类型__rb_tree_node_base 仅包含树结构指针(父/左/右)和颜色信息


3. 核心移动操作:increment()(++操作)
void increment() {if (node->right != 0) {         // 情况1:存在右子树node = node->right;         // 先移动到右子节点while (node->left != 0)     // 再向左走到尽头node = node->left;} else {                        // 情况2:无右子树base_ptr y = node->parent;while (node == y->right) {  // 回溯直到不再是右子节点node = y;y = y->parent;}if (node->right != y)       // 特殊边界检测(防止回环)node = y;}
}

移动逻辑

  1. 有右子树:右子树的最左节点(中序后继)

  2. 无右子树:回溯到第一个左祖先节点

  3. 边界处理node->right != y 防止指向 header 节点时形成环


4. 核心移动操作:decrement()(--操作)
void decrement() {if (node->color == __rb_tree_red &&  // 情况1:header节点特殊处理node->parent->parent == node) {  // header特征:自环结构node = node->right;              // 指向最大值节点} else if (node->left != 0) {        // 情况2:存在左子树base_ptr y = node->left;while (y->right != 0)            // 左子树的最右节点y = y->right;node = y;} else {                             // 情况3:无左子树base_ptr y = node->parent;while (node == y->left) {        // 回溯直到不再是左子节点node = y;y = y->parent;}node = y;}
}

移动逻辑

  1. header 节点:直接跳转到最大值节点(通过 node->right

  2. 有左子树:左子树的最右节点(中序前驱)

  3. 无左子树:回溯到第一个右祖先节点


5. 具体迭代器关键实现
reference operator*() const { return link_type(node)->value_field;  // 实际值访问
}pointer operator->() const { return &(operator*());               // 指针访问
}self& operator++() { increment();  // 调用基础迭代器的移动逻辑return *this;
}

类型转换技巧

return link_type(node)->value_field;
  • link_type = __rb_tree_node<Value>*

  • 安全转换:将 __rb_tree_node_base* 转为具体节点类型

  • 值访问:通过 value_field 获取实际存储数据


6. 迭代器特征设计
typedef Value value_type;
typedef Ref reference;   // 如 Value&
typedef Ptr pointer;     // 如 Value*

模板参数化

  • 三参数设计<Value, Ref, Ptr> 支持常量/非常量迭代器

    • 普通迭代器:__rb_tree_iterator<Value, Value&, Value*>

    • 常量迭代器:__rb_tree_iterator<Value, const Value&, const Value*>


7. 边界检测关键细节

header 节点识别

node->color == __rb_tree_red && 
node->parent->parent == node
  • 颜色特征:header 节点固定为红色

  • 结构特征:形成 header->parent->parent == header 的自环

递增边界保护

if (node->right != y)  // 防止指向自身node = y;
  • 确保从最大值节点递增时正确跳转到 header(end迭代器)


iterator实现思路分析

基本实现框架

iterator的实现采用与list类似的思路,通过一个类封装结点的指针,并重载运算符模拟指针行为。核心实现包括:

  1. 封装一个指向树结点的指针成员变量
  2. 重载operator*和operator->用于访问数据
  3. 重载operator++和operator--实现遍历
  4. 重载比较运算符实现迭代器比较

中序遍历实现关键点

map和set的迭代器遵循中序遍历顺序(左子树->根结点->右子树),begin()返回树的最左结点(中序第一个结点)。

operator++实现逻辑
  1. 右子树非空情况

    • 当前结点访问完毕后,下一个要访问的是右子树的中序第一个结点
    • 实现方式:找到右子树的最左结点
    if(node->right) {node = node->right;while(node->left) node = node->left;
    }
    
  2. 右子树为空情况

    • 需要向上查找祖先结点
    • 如果当前结点是父结点的左孩子,则下一个访问父结点
    • 如果当前结点是父结点的右孩子,继续向上查找直到找到某结点是其父结点的左孩子
    else {Node* parent = node->parent;while(parent && node == parent->right) {node = parent;parent = parent->parent;}node = parent;
    }
    
operator--实现逻辑

operator--与operator++逻辑相反,按照右子树->根结点->左子树的顺序:

  1. 若左子树非空,则找到左子树的最右结点
  2. 若左子树为空,则向上查找直到找到某结点是其父结点的右孩子

end()处理方案

  1. 简单方案:使用nullptr作为end()标志

    • 当遍历到最后结点时(如图中50),继续++会返回nullptr
    • --begin()同样返回nullptr
    • 需要特殊处理--end()使其指向树的最右结点
  2. STL方案:使用哨兵头结点

    • 增加一个专用头结点,其parent指向根结点
    • 左指针指向树的最小结点,右指针指向树的最大结点
    • 根结点的parent也指向该头结点
    • 这种设计使--end()可以直接指向最大结点

类型约束实现

  1. set迭代器

    template<class K>
    class set {RBTree<K, const K, SetKeyOfT> _t;// const K - 保证set元素的不可修改性
    };
    
  2. map迭代器

    template<class K, class V> 
    class map {RBTree<K, pair<const K, V>, MapKeyOfT> _t;// pair<const K, V> - 键值对,键为const保证不可修改
    };
    
  • set存储单个元素(const K),保证元素不可修改
  • map存储键值对(pair<const K, V>),保证键不可修改

完整实现注意事项

  1. 边界条件处理:begin()/end()/++/--的边界情况
  2. const迭代器支持
  3. 迭代器失效问题处理
  4. 与容器其他操作的兼容性


我们实现Mymap和Myset迭代器时,使用nullptr作为end(),如果仿照STL中使用头节点的话,我们自己封装的红黑树就需要改动,旋转之后还需要维护,对于我们来说改动比较多,成本比较高,所以这里我们不去仿照STL搞一个头节点

关于这两种方法实现的优缺点:

1. 我的实现:nullptr 作为 end() 标志
// 查找示例
Node* Find(const K& key) {Node* cur = _root;while (cur) { // 依赖 nullptr 判断边界if (key < cur->_data) cur = cur->_left;else if (key > cur->_data) cur = cur->_right;else return cur;}return nullptr; // end() 等价于 nullptr
}

✅ 优点

  1. 内存精简

    • 无额外内存开销(节省一个头节点)

    • 空树时仅需 _root = nullptr

  2. 实现简单

    • 插入/删除逻辑无需维护额外指针

    • 迭代器只需存储当前节点指针

  3. 遍历效率

    • 普通遍历操作与哨兵方案性能相当

❌ 缺点

  1. 边界处理复杂

    // 迭代器递减操作需特殊处理根节点
    void decrement() {if (node == _root) { // 需要特殊处理根节点边界}// ...其他逻辑
    }
  2. 无法直接访问极值

    • 获取 begin()(最小节点)需 O(log n) 遍历

    • 没有缓存最大/最小节点,每次需重新查找

  3. 迭代器失效风险

    • end() 迭代器 (nullptr) 无法进行 -- 操作

    • 无法实现标准要求的 --end() 获取最大值


2. STL 哨兵头结点方案
// STL 头结点结构示例
struct __rb_tree_node_base {__rb_tree_node_base* parent;__rb_tree_node_base* left;   // 指向最小节点__rb_tree_node_base* right;  // 指向最大节点
};class rb_tree {
protected:link_type header; // 哨兵头结点// ...
};

✅ 优点

  1. 极值访问 O(1)

    • begin() = header->left

    • end() = header

    • rbegin() = header->right

  2. 边界处理统一

    // 迭代器递增无需特殊判断
    void increment() {if (node->right) {// 正常右子树处理} else {// 统一回溯逻辑(含header处理)}
    }
  3. 符合STL标准

    • 支持 --end() 获取最大值

    • 满足双向迭代器所有要求

  4. 空树处理优雅

    // 空树时形成自环
    header->parent = nullptr;
    header->left = header;
    header->right = header;

❌ 缺点

  1. 内存开销

    • 额外 3 个指针 + 颜色字段的内存占用

    • 小型容器相对开销较大

  2. 实现复杂度

    • 插入/删除需维护header指针:

    // 插入新节点后需更新极值
    if (new_node < header->left) header->left = new_node;
    if (new_node > header->right)header->right = new_node;
  3. 指针维护成本

    • 旋转操作需额外更新header关联

由于nullptr为end()时,在end()--时会有风险,所以我们在实现时需要考虑这种特殊情况。

代码实现和梳理:

代码基础架构:
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}
};
关键点分析
  1. 模板参数设计

    • T:节点数据类型(如 pair<K, V> 或直接 K

    • Ref:引用类型(T& 或 const T&

    • Ptr:指针类型(T* 或 const T*

    • 作用:通过模板参数实现常量/非常量迭代器的统一实现

  2. 节点访问操作

    Ref operator*() { return _node->_data; }  // 返回数据引用
    Ptr operator->() { return &_node->_data; } // 返回数据指针
    • 提供标准迭代器访问接口

    • 支持 *iter 和 iter->member 语法

  3. 迭代器比较

    bool operator!=(const Self& s) const { return _node != s._node; }
    bool operator==(const Self& s) const { return _node == s._node; }
    • 直接比较节点指针地址

    • 简单高效,符合迭代器等价性要求


operator++ 实现
执行过程分析
情况1:当前节点有右子树(直接后继存在)
if (_node->_right) 
{// 步骤1:进入右子树Node* minright = _node->_right;// 步骤2:找到右子树的最左节点while (minright->_left){minright = minright->_left;}// 步骤3:更新当前节点_node = minright;
}
  1. 进入右子树:从当前节点移动到其右子节点

  2. 向左遍历到底:在右子树中持续向左遍历,直到最左叶子节点

  3. 更新迭代器:将迭代器指向找到的最左节点

图示过程

      [A]         // 当前节点\[B]      // 进入右子树/ \[C]   [D]   // 在右子树中/ \         // 向左遍历[E]  [F]       // 找到最左节点 [E]
情况2:当前节点无右子树(回溯寻找祖先)
else 
{Node* cur = _node;Node* parent = cur->_parent;// 向上回溯直到找到左祖先while (parent && parent->_left != cur){cur = parent;parent = cur->_parent;}_node = parent; // 更新为左祖先或nullptr
}
  1. 初始化指针

    • cur = 当前节点

    • parent = 当前节点的父节点

  2. 回溯循环

    • 当 cur 是 parent 的右子节点时继续回溯

    • 移动:cur → parentparent → 祖父节点

  3. 终止条件

    • 找到 cur 是 parent 左子节点的位置

    • 或回溯到根节点后(parent == nullptr

  4. 更新迭代器

    • 指向找到的左祖先节点

    • 若无满足条件的祖先,设为 nullptr (表示 end())

图示过程

      [G]        // 最终目标祖先/[P]         // cur是G的左子节点\[C]        // 当前节点为右子节点\[A]      // 需要回溯的节点路径\[B]    // 当前节点
  1. 从 [B] 开始回溯

  2. [B] 是 [A] 的右子 → 继续

  3. [A] 是 [C] 的右子 → 继续

  4. [C] 是 [P] 的右子 → 继续

  5. [P] 是 [G] 的左子 → 停止,返回 [G]


具体代码:
Self& operator++()
{if (_node->_right) // 右子树不为空,找右子树中序第一个节点,也就是右子树最小节点{Node* minright = _node->_right;while (minright->_left){minright = minright->_left;}_node = minright;}else // 右子树为空,找孩子是父亲左的那个祖先节点{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left != cur){cur = parent;parent = cur->_parent;}_node = parent; // 更新为左祖先或nullptr}return *this;
}

operator-- 的实现
执行过程分析
情况1:end() 迭代器 (_node == nullptr)
if (_node == nullptr)
{Node* maxright = _root;// 使用 _root 开始遍历while (maxright->_right) // 向右遍历到底{maxright = maxright->_right;}_node = maxright; // 指向最大节点
}
  1. 特殊处理:当迭代器处于 end() (nullptr) 时

  2. 查找最大值:从根节点开始持续向右遍历

  3. 更新节点:指向找到的最右节点(树的最大值)

情况2:左子树存在
else if (_node->_left)
{Node* maxright = _node->_left;while (maxright->_right) // 在左子树中向右遍历{maxright = maxright->_right;}_node = maxright; // 指向左子树的最右节点
}
  1. 进入左子树:移动到当前节点的左子节点

  2. 向右遍历:在左子树中持续向右,直到最右节点

  3. 更新节点:指向找到的最右节点

示例

  [5]        // 当前节点/
[3]        // 进入左子树\[4]     // 向右遍历至最右节点
情况3:左子树不存在(回溯寻找右祖先)
else
{Node* cur = _node;Node* parent = cur->_parent;// 回溯直到找到右祖先while (parent && parent->_right != cur){cur = parent;parent = cur->_parent;}_node = parent; // 更新为右祖先或nullptr
}
  1. 初始化指针

    • cur = 当前节点

    • parent = 当前节点的父节点

  2. 回溯循环

    • 当 cur 是 parent 的左子节点时继续回溯

    • 移动:cur → parentparent → 祖父节点

  3. 终止条件

    • 找到 cur 是 parent 右子节点的位置

    • 或回溯到根节点后(parent == nullptr

  4. 更新迭代器

    • 指向找到的右祖先节点

    • 若无满足条件的祖先,设为 nullptr (表示 rend())

示例

    [P]        // 目标祖先(右祖先)\[C]       // cur是P的右子节点/[A]         // 当前节点起点/[B]           // 需要回溯的节点
  1. 从 [B] 开始回溯

  2. [B] 是 [A] 的左子 → 继续

  3. [A] 是 [C] 的左子 → 继续

  4. [C] 是 [P] 的右子 → 停止

  5. 返回 [P]


具体代码:
Node* _node;
Node* _root;// 处理end()--情况,需要从根节点开始遍历最右节点RBTreeIterator(Node* node, Node* root):_node(node),_root(root)
{}
Self& operator--()
{if (_node == nullptr) // end()--,特殊情况处理{// 找到树的最右节点,也就是最大节点Node* maxright = _root;// 使用 _root 开始遍历while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else if (_node->_left) // 左子树不为空,找左子树最右节点,也就是最大节点{Node* maxright = _node->_left;while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else // 左子树不为空,找孩子是父亲右的那个祖先{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}

复用红黑树迭代器实现Mymap和Myset的迭代器功能

template<class K, class T, class KeyOfT>
class RBTree
{using Node = RBTreeNode<T>;public:using Iterator = RBTreeIterator<T, T&, T*>;using ConstIterator = RBTreeIterator<T, const T&, const T*>;Iterator Begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return ConstIterator(cur, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}private:Node* _root = nullptr;
};

在上文中还有一点我们需要注意,也就是在类型约束实现中,在STL源码中为了不让外界修改我们key,使用了const来保证键的不可修改,那么这里我们也可以这么做

Mymap.h

namespace Ro
{template<class K, class V>class Mymap{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}bool insert(const pair<K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}

Myset.h

namespace Ro
{template<class K>class Myset{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}bool insert(const K& key){return _t.Insert(key);}private:RBTree<K, const K, SetKeyOfT> _t;};
}

2.3 operator[]

• map要实现[]运算符,关键在于改进insert函数的返回值。需要将RBtree中的insert函数返回值类型修改为pair<Iterator, bool>

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

• 在insert功能完善后,[]运算符的实现就变得简单明了,具体实现可参考以下代码。

修改insert的返回值类型

RBtree.h

pair<Iterator, bool> Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//return pair<Iterator, bool>(Iterator(_root, _root), true);return { Iterator(_root, _root), true }; // 隐式类型转换}KeyOfT kt;Node* parent = nullptr;Node* cur = _root;while (cur){if (kt(cur->_data) < kt(data)){parent = cur;cur = cur->_right;}else if (kt(cur->_data) > kt(data)){parent = cur;cur = cur->_left;}else{return { Iterator(cur, _root), false }; //插入失败返回当前已存在节点的迭代器}}cur = new Node(data);Node* newnode = cur; // 用于返回新插入的节点迭代器if (kt(parent->_data) < kt(data)){parent->_right = cur;}else{parent->_left = cur;}//链接父亲节点cur->_parent = parent;// 父节点为红色,需要颜色修正while (parent && parent->_col == RED){Node* grandfather = parent->_parent; // 祖父节点必存在(因父为红,不可能是根)// 分父节点是祖父的左/右孩子两种情况处理if (grandfather->_left == parent){//		g(B)//	p(R)	   u(分情况)Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED) // 情况1:变色处理{//		g(B)//	p(R)	   u(存在且为红色)//c(R)// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 情况2,3:旋转 + 变色{if (parent->_left == cur) // 右单旋 + 变色{//		g(B)//	p(R)	   u(不存在或为黑色)//c(R)RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else // 左右双旋 + 变色{//		g(B)//	p(R)	   u(不存在或为黑色)//    c(R)RotateL(parent);RotateR(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//此时旋转之后的子树根节点为parent或cur,但都变为黑色,更新到黑结束break;}}else{//		      g(B)//	u(分情况)		 p(R)Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED) // 情况1:变色处理{//			  g(B)//	u(存在且为红色)		p(R)//							c(R)// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 情况2,3:旋转 + 变色{if (parent->_right == cur) // 左单旋 + 变色{//			  g(B)//	u(不存在或为黑色)		p(R)//							c(R)RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else // 右左双旋 + 变色{//			  g(R)//	u(不存在或为黑色)		p(R)//					c(R)RotateR(parent);RotateL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//此时旋转之后的子树根节点为parent或cur,但都变为黑色,更新到黑结束break;}}}// 暴力处理:无论是否处理到根节点,都直接把根节点变为黑色(符合根节点为黑色规则)_root->_col = BLACK;//return pair<Iterator, bool>(Iterator(newnode, _root), true);return { Iterator(newnode, _root), true }; // 隐式类型转换
}

Mymap.h

pair<iterator, bool> insert(const pair<K, V>& kv)
{return _t.Insert(kv);
}

Myset.h

pair<iterator, bool> insert(const K& key)
{return _t.Insert(key);
}

map 下标运算符 operator[] 实现解析

通过键值 key 访问对应的值 V,如果键不存在则自动插入新元素

V& operator[](const K& key)
{// 尝试插入键值对 (key, 默认值)pair<iterator, bool> ret = insert({ key, V() });  // 返回键对应值的引用return ret.first->second;
}
执行流程详解:
  1. 插入操作

    pair<iterator, bool> ret = insert(key, V());
    • 尝试插入键值对 (key, V())

    • V() 创建值类型的默认构造对象(如 int() 返回 0string() 返回空串)

    • 返回值 ret 包含:

      • ret.first:指向元素的迭代器

      • ret.second:插入是否成功(true=新插入,false=已存在)

  2. 返回值引用

    return ret.first->second;
    • 通过迭代器获取值的引用

    • 无论是否新插入,都返回键对应的值引用

关键特性:
  1. 自动插入机制

    • 当键不存在时:自动创建 (key, V()) 并返回新值的引用

    • 当键存在时:直接返回已有值的引用

  2. 返回值可修改

    map<string, int> ages;
    ages["Alice"] = 25;  // 自动创建并赋值
    ages["Bob"]++;       // 自动创建0然后自增
  3. 等价写法

    iterator it = ret.first;
    return it->second;
    • 等价于直接 ret.first->second


测试

测试Myset

void test_set()
{Ro::Myset<int> s;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){s.insert(e);}for (auto e : s){cout << e << " ";}cout << endl;
}

运行结果:

测试Mymap

void test_map()
{Ro::Mymap<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左边" });dict.insert({ "right", "右边" });dict["left"] = "左边,剩余";dict["insert"] = "插入";dict["string"];for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}cout << endl;Ro::Mymap<string, string>::iterator it = dict.begin();while (it != dict.end()){// 不能修改first,可以修改second//it->first += 'x';it->second += 'x';cout << it->first << ":" << it->second << endl;++it;}cout << endl;
}

运行结果:


我们这里封装红黑树实现的Mymap和Myset并不是很完善,这里只是简单实现了一下,让Mymap和Myset能够实现一些正常的插入遍历的操作,可以跑起来。例如删除,拷贝,赋值等一些操作没有实现,感兴趣的可以自己去实现一下。

代码:

RBTree.h

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED)          // 新节点默认红色(符合红黑树插入规则){}T _data;          RBTreeNode<T>* _left; // 左子节点RBTreeNode<T>* _right;// 右子节点RBTreeNode<T>* _parent; // 父节点Colour _col;             // 节点颜色
};template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;Node* _root;// 处理end()--情况,需要从根节点开始遍历最右节点RBTreeIterator(Node* node, Node* root):_node(node),_root(root){}Self& operator++(){if (_node->_right) // 右子树不为空,找右子树中序第一个节点,也就是右子树最小节点{Node* minright = _node->_right;while (minright->_left){minright = minright->_left;}_node = minright;}else // 右子树为空,找孩子是父亲左的那个祖先节点{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left != cur){cur = parent;parent = cur->_parent;}_node = parent; // 更新为左祖先或nullptr}return *this;}Self& operator--(){if (_node == nullptr) // end()--,特殊情况处理{// 找到树的最右节点,也就是最大节点Node* maxright = _root;// 使用 _root 开始遍历while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else if (_node->_left) // 左子树不为空,找左子树最右节点,也就是最大节点{Node* maxright = _node->_left;while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else // 左子树不为空,找孩子是父亲右的那个祖先{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right != cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s) const{return _node != s._node;}bool operator==(const Self& s) const{return _node == s._node;}
};template<class K, class T, class KeyOfT>
class RBTree
{using Node = RBTreeNode<T>;
public:using Iterator = RBTreeIterator<T, T&, T*>;using ConstIterator = RBTreeIterator<T, const T&, const T*>;Iterator Begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}ConstIterator Begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return ConstIterator(cur, _root);}ConstIterator End() const{return ConstIterator(nullptr, _root);}pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//return pair<Iterator, bool>(Iterator(_root, _root), true);return { Iterator(_root, _root), true }; // 隐式类型转换}KeyOfT kt;Node* parent = nullptr;Node* cur = _root;while (cur){if (kt(cur->_data) < kt(data)){parent = cur;cur = cur->_right;}else if (kt(cur->_data) > kt(data)){parent = cur;cur = cur->_left;}else{return { Iterator(cur, _root), false }; //插入失败返回当前已存在节点的迭代器}}cur = new Node(data);Node* newnode = cur; // 用于返回新插入的节点迭代器if (kt(parent->_data) < kt(data)){parent->_right = cur;}else{parent->_left = cur;}//链接父亲节点cur->_parent = parent;// 父节点为红色,需要颜色修正while (parent && parent->_col == RED){Node* grandfather = parent->_parent; // 祖父节点必存在(因父为红,不可能是根)// 分父节点是祖父的左/右孩子两种情况处理if (grandfather->_left == parent){//		g(B)//	p(R)	   u(分情况)Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED) // 情况1:变色处理{//		g(B)//	p(R)	   u(存在且为红色)//c(R)// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 情况2,3:旋转 + 变色{if (parent->_left == cur) // 右单旋 + 变色{//		g(B)//	p(R)	   u(不存在或为黑色)//c(R)RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else // 左右双旋 + 变色{//		g(B)//	p(R)	   u(不存在或为黑色)//    c(R)RotateL(parent);RotateR(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//此时旋转之后的子树根节点为parent或cur,但都变为黑色,更新到黑结束break;}}else{//		      g(B)//	u(分情况)		 p(R)Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED) // 情况1:变色处理{//			  g(B)//	u(存在且为红色)		p(R)//							c(R)// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else // 情况2,3:旋转 + 变色{if (parent->_right == cur) // 左单旋 + 变色{//			  g(B)//	u(不存在或为黑色)		p(R)//							c(R)RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else // 右左双旋 + 变色{//			  g(R)//	u(不存在或为黑色)		p(R)//					c(R)RotateR(parent);RotateL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}//此时旋转之后的子树根节点为parent或cur,但都变为黑色,更新到黑结束break;}}}// 暴力处理:无论是否处理到根节点,都直接把根节点变为黑色(符合根节点为黑色规则)_root->_col = BLACK;//return pair<Iterator, bool>(Iterator(newnode, _root), true);return { Iterator(newnode, _root), true }; // 隐式类型转换}// 右单旋void RotateR(Node* parent){Node* subL = parent->_left;// parent的左子节点(旋转后的新根)Node* subLR = subL->_right;// subL的右子节点(可能为空)// 旋转节点subL->_right = parent;parent->_left = subLR;// 维护父指针Node* pParent = parent->_parent;parent->_parent = subL;if (subLR) //若subLR存在,更新其父指针,避免堆空指针解引用{subLR->_parent = parent;}subL->_parent = pParent;// 维护parent的父节点if (parent == _root) // parent为根节点的情况{_root = subL;}else // parent是一棵局部子树的情况{if (pParent->_left == parent){pParent->_left = subL;}else{pParent->_right = subL;}}}// 左单旋void RotateL(Node* parent){Node* subR = parent->_right;// parent的右子节点(旋转后的新根)Node* subRL = subR->_left;// subR的左子节点(可能为空)// 旋转节点subR->_left = parent;parent->_right = subRL;// 维护父指针Node* pParent = parent->_parent;parent->_parent = subR;if (subRL) //若subRL存在,更新其父指针,避免堆空指针解引用{subRL->_parent = parent;}subR->_parent = pParent;// 维护parent的父节点if (parent == _root) // parent为根节点的情况{_root = subR;}else // parent是一棵局部子树的情况{if (pParent->_left == parent){pParent->_left = subR;}else{pParent->_right = subR;}}}Iterator Find(const K& key){KeyOfT kt;Node* cur = _root;while (cur){if (kt(cur->_data) < key){cur = cur->_right;}else if (kt(cur->_data) > key){cur = cur->_left;}else{return Iterator(cur, _root);}}return End();}int Height(){return _Height(_root);}int Size(){return _Size(_root);}
private:int _Size(Node* root){if (root == nullptr) return 0;int leftSize = _Size(root->_left);int rightSize = _Size(root->_right);return leftSize + rightSize + 1;}int _Height(Node* root){if (root == nullptr) return 0;int leftHigh = _Height(root->_left);int rightHigh = _Height(root->_right);return leftHigh > rightHigh ? leftHigh + 1 : rightHigh + 1;}private:Node* _root = nullptr;
};

Mymap.h

#pragma once#include "RBTree.h"namespace Ro
{template<class K, class V>class Mymap{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}V& operator[](const K& key){// 尝试插入键值对 (key, 默认值)pair<iterator, bool> ret = insert({ key, V() });//iterator it = ret.first;//return it->second; //返回_data中键对应的值 // 等价写法return ret.first->second; // 返回键对应值的引用}iterator find(const K& key){return _t.Find(key);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};
}

Myset.h

#pragma once#include "RBTree.h"namespace Ro
{template<class K>class Myset{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}iterator find(const K& key){return _t.Find(key);}private:RBTree<K, const K, SetKeyOfT> _t;};
}

相关文章:

  • Trust Tickets(跨域信任票据):内网渗透中的Kerberos信任票据滥用技术
  • 编译rustdesk,使用flutter、hwcodec硬件编解码
  • 龙虎榜——20250530
  • Ubuntu本地文件上传github(版本控制)
  • 2025年渗透测试面试题总结-匿名[校招]攻防研究员(应用安全)(题目+回答)
  • 《智慧医疗分级评价方法及标准(2025版)》征求意见函全面解读:人工智能医疗应用的评价体系与指南方向
  • Dify理论+部署+实战
  • python常用库-pandas、Hugging Face的datasets库(大模型之JSONL(JSON Lines))
  • 使用matlab读取txt文件中的2进制数据
  • 中联教育 - 嵌入式BI助力财经数据分析服务
  • 相机--RGB相机
  • 《TCP/IP 详解 卷1:协议》第3章:链路层
  • 在 Linux 上构建 Kubernetes 单节点集群:Minikube 安装与实战指南
  • 5分钟学会网络服务搭建,飞凌i.MX9352 + Linux 6.1实战示例
  • C++ TCP程序增加TLS加密认证
  • DPO(Direct Preference Optimization)详解-1
  • VirtualBox给Rock Linux9.x配置网络
  • vueflow
  • shell中与>和<相关的数据流重定向操作符整理
  • Spring Cloud Alibaba 学习 —— 简单了解常用技术栈
  • 网站建设必须要备案吗/网站百度权重
  • 网站开发实训总结/百度指数官网移动版
  • 网站专栏建设方案/自媒体有哪些平台
  • 文化馆 网站 设计/推广app的软文案例
  • 公众号如何推广产品/seo工作
  • wordpress文章后添加除非/武汉seo首页