
观众老爷们大家好 我是邪修KING 本文属于系列C++ 进阶篇 ,欢迎来到C++进阶篇博客 C++重点语法运用! >本文属于 **《C++ 进阶篇系统教程》第 7 篇**,上一篇我们讲透了 红黑 树的自平衡原理,今天我们深入 STL 的核心底层 ------红黑树。红黑树是map/set的底层实现,它能同时支持 key 模型和 key/value 模型的核心秘密是仿函数重载,而让它能像容器一样被遍历和使用的关键是迭代器设计,map最常用的\[\]运算符更是基于红黑树接口的巧妙封装。
很多人学红黑树只关注旋转和变色,却忽略了 STL 红黑树最精妙的工程设计:
1.用KeyOfT 仿函数 解耦比较逻辑和存储类型,一颗树同时实现 set 和 map
2.用双向迭代器封装 中序遍历逻辑,让红黑树支持范围 for 和标准算法
3.用 **\[\]运算符 ** 封装插入 + 查找逻辑,让 map 的使用体验媲美字典
本文将从这三个核心维度出发,结合 SGI-STL 源码的设计思想,一步步拆解红黑树的完整实现,搞懂每一个设计背后的考量。
一、(回顾)先搞懂:什么是仿函数?
1.1 仿函数的定义
仿函数(Functor)也叫函数对象,是一个重载了operator()运算符的类,它可以像函数一样被调用。
cpp
// 一个简单的仿函数
class Add {
public:
int operator()(int a, int b) {
return a + b;
}
};
int main() {
Add add;
cout << add(1, 2) << endl; // 像函数一样调用,输出:3
return 0;
}
1.2 仿函数 vs 函数指针
仿函数相比函数指针有三个核心优势:
1.可以保存状态 :仿函数是类,可以有成员变量,保存调用时的状态
2.可以内联 :编译器可以内联仿函数的调用,性能更高
3.类型安全 :仿函数有明确的类型,而函数指针容易出现类型不匹配的问题
这也是为什么 STL 几乎所有的算法和容器都使用仿函数而不是函数指针的原因。
二、红黑树的核心设计难题:如何同时支持 set 和 map?
set 是key 模型 ,只存储单个 key;map 是key/value 模型 ,存储pair<const Key, Value>。如果我们分别为 set 和 map 写一颗红黑树,会导致大量的代码重复。
SGI-STL 的解决方案非常巧妙:
红黑树不关心存储的数据类型,只关心如何从存储的数据中提取出用于比较的 key 。
这个 "提取 key" 的逻辑,就交给KeyOfT 仿函数来实现。红黑树只需要通过这个仿函数,从任意类型的数据中提取出 key,然后进行比较和排序。
2.1 红黑树的模板参数设计
SGI-STL 的红黑树有 5 个模板参数,其中最核心的是前 4 个:
cpp
template <
class Key, // 用于比较的key的类型
class Value, // 红黑树节点存储的数据类型(set是Key,map是pair<const Key, Value>)
class KeyOfValue, // 仿函数:从Value中提取出Key
class Compare = less<Key>, // 比较函数:比较两个Key的大小
class Alloc = alloc
>
class rb_tree;
Key:用于比较的 key 的类型,find/erase等函数的参数类型
Value:节点实际存储的数据类型
KeyOfValue:仿函数,接收一个Value类型的参数,返回其中的Key
Compare:比较两个Key大小的仿函数,默认是less(升序)
2.2 set 和 map 如何实例化红黑树
set 的实例化
set 只存储 key,所以Value就是Key,KeyOfValue仿函数直接返回传入的参数:
cpp
#include"RBTree.h"
namespace bit
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;
iterator begin()
{
return _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);
}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
}
};
map 的实例化
map 存储pair<const Key, Value>,所以KeyOfValue仿函数需要返回 pair 的第一个元素:
cpp
#include"RBTree.h"
namespace bit
{
template<class K, class V>
class map
{
//重点研究!!!仿函数,仿函数是一个类,重载了operator()运算符,
// 使得它的对象可以像函数一样被调用
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;
//问题一迭代器的类型,迭代器访问的元素类型是pair<const K, V>,
// 而不是pair<K, V>,因为map中的key是不能修改的,所以要加上const修饰符
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)
{
pair<iterator, bool> ret = insert({ key, V() });
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
通过不同的KeyOfT 仿函数,同一个红黑树类就可以同时支持 set 和 map 两种完全不同的模型,没有任何代码重复。
三、仿函数在红黑树核心操作中的应用
红黑树的所有核心操作(插入、查找、删除)都依赖于KeyOfT仿函数来提取 key 进行比较。下面我们以插入和查找为例,看仿函数是如何被使用的。
3.1 插入操作中的仿函数应用
插入操作的核心是找到新节点应该插入的位置,这需要不断比较新节点的 key 和当前节点的 key。
cpp
enum Colour
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
RBTreeNode(const T& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
// 实现步骤:
// 1、实现红⿊树
// 2、封装map和set框架,解决KeyOfT
// 3、iterator
// 4、const_iterator
// 5、key不⽀持修改的问题
// 6、operator[]
template<class K, class T, class KeyOfT>
class RBTree
{
private:
typedef RBTreeNode<T> Node;
Node* _root = nullptr;
public:
bool Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(data);
Node* newnode = cur;
// 新增结点。颜⾊给红⾊
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//...
return true;
}
}
注意:Find函数的参数是Key类型,而不是Value类型,这就是为什么红黑树需要第一个模板参数Key------ 它是find/erase等函数的参数类型。
四、红黑树迭代器实现思路详解(核心难点)
迭代器是容器的 "访问接口",它让我们可以用统一的方式遍历不同的容器。红黑树的迭代器是双向迭代器 ,支持++和--操作,按照中序遍历的顺序访问节点(左→根→右)。
4.1 迭代器的整体设计框架
红黑树迭代器的设计思路和list迭代器完全一致:
用一个类封装节点的指针,通过重载*、->、++、--等运算符,让迭代器像指针一样访问节点的数据。
cpp
template <typename T, typename Ref, typename 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)
{}
// 重载运算符...
};
4.2 核心难点:operator++ 的实现
iterator实现思路分析
• iterator实现的⼤框架跟list的iterator思路是⼀致的,⽤⼀个类型封装结点的指针,再通过重载运算
符实现,迭代器像指针⼀样访问的⾏为。
• 这⾥的难点是operator++和operator--的实现。之前使⽤部分,我们分析了,map和set的迭代器⾛的是中序遍历,左⼦树->根结点->右⼦树,那么begin()会返回中序第⼀个结点的iterator也就是10所在结点的迭代器。
• 迭代器++的核⼼逻辑就是不看全局,只看局部,只考虑当前中序局部要访问的下⼀个结点。
• 迭代器++时,如果it指向的结点的右⼦树不为空,代表当前结点已经访问完了,要访问下⼀个结点是右⼦树的中序第⼀个,⼀棵树中序第⼀个是最左结点,所以直接找右⼦树的最左结点即可。
• 迭代器++时,如果it指向的结点的右⼦树空,代表当前结点已经访问完了且当前结点所在的⼦树也访问完了,要访问的下⼀个结点在当前结点的祖先⾥⾯,所以要沿着当前结点到根的祖先路径向上找。
• 如果当前结点是⽗亲的左,根据中序左⼦树->根结点->右⼦树,那么下⼀个访问的结点就是当前结点的⽗亲;如下图:it指向25,25右为空,25是30的左,所以下⼀个访问的结点就是30。
• 如果当前结点是⽗亲的右,根据中序左⼦树->根结点->右⼦树,当前当前结点所在的⼦树访问完了,当前结点所在⽗亲的⼦树也访问完了,那么下⼀个访问的需要继续往根的祖先中去找,直到找到孩⼦是⽗亲左的那个祖先就是中序要问题的下⼀个结点。如下图:it指向15,15右为空,15是10的右,15所在⼦树话访问完了,10所在⼦树也访问完了,继续往上找,10是18的左,那么下⼀个访问的结点就是18。
• end()如何表⽰呢?如下图:当it指向50时,++it时,50是40的右,40是30的右,30是18的右,18到根没有⽗亲,没有找到孩⼦是⽗亲左的那个祖先,这是⽗亲为空了,那我们就把it中的结点指针置为nullptr,我们⽤nullptr去充当end。需要注意的是stl源码空,红⿊树增加了⼀个哨兵位头结点做为end(),这哨兵位头结点和根互为⽗亲,左指向最左结点,右指向最右结点。相⽐我们⽤nullptr作为end(),差别不⼤,他能实现的,我们也能实现。只是--end()判断到结点时空,特殊处理⼀下,让迭代器结点指向最右结点。具体参考迭代器--实现。
• 迭代器--的实现跟++的思路完全类似,逻辑正好反过来即可,因为他访问顺序是右⼦树->根结点->左⼦树,具体参考下⾯代码实现。
• set的iterator也不⽀持修改,我们把set的第⼆个模板参数改成constK即可,RBTree<K, const K,SetKeyOfT> _t;
• map的iterator不⽀持修改key但是可以修改value,我们把map的第⼆个模板参数pair的第⼀个参数改成constK即可,RBTree<K, pair<const K, V>, MapKeyOfT> _t;

1、当右子树存在时
因为迭代器的++方式是左子树根结点右子树,而右子树又分为左子树根结点右子树
所以右子树存在时,++迭代器,迭代器就应该走到右子树的最左结点
2、当右子树不存在时
因为迭代器++方式是左子树根结点右子树,所以当右子树不存在时,就代表该子树已经走完了,那么就需要去找祖先结点中子为父左的父结点
operator++的实现
cpp
// 前置++:中序遍历的下一个节点
Self& operator++() {
if (_node->_right != nullptr) {
// 情况1:有右子树,下一个节点是右子树的最左节点
Node* leftMost = _node->_right;
while (leftMost->_left != nullptr) {
leftMost = leftMost->_left;
}
_node = leftMost;
} else {
// 情况2:没有右子树,向上找第一个孩子是左孩子的祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent != nullptr && cur == parent->_right) {
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
4.3 end () 的表示与 operator-- 的实现
end () 的表示
我们用nullptr表示end(),也就是遍历完所有节点后的位置。当++it向上找完所有祖先都没有找到下一个节点时,就把_node置为nullptr,也就是end()。
注意:STL 源码中用了一个哨兵位头结点作为end(),这个哨兵位和根节点互为父亲,左指向整棵树的最左节点,右指向最右节点。我们用nullptr作为end()更简单,功能完全一致,只是需要对--end()做特殊处理。
operator-- 的实现
--的逻辑和++完全相反,是找到中序遍历的上一个节点:
1、如果当前节点是end()(_node == nullptr),特殊处理:上一个节点是整棵树的最右节点
2、如果当前节点有左子树,上一个节点是左子树的最右节点
3、如果当前节点没有左子树,向上找第一个孩子是右孩子的祖先,这个父亲就是上一个节点
operator-- 代码实现
cpp
// 前置--:中序遍历的上一个节点
Self& operator--() {
if (_node == nullptr) {
// 特殊情况:--end(),返回整棵树的最右节点
Node* rightMost = _root;
while (rightMost != nullptr && rightMost->_right != nullptr) {
rightMost = rightMost->_right;
}
_node = rightMost;
} else if (_node->_left != nullptr) {
// 情况1:有左子树,上一个节点是左子树的最右节点
Node* rightMost = _node->_left;
while (rightMost->_right != nullptr) {
rightMost = rightMost->_right;
}
_node = rightMost;
} else {
// 情况2:没有左子树,向上找第一个孩子是右孩子的祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent != nullptr && cur == parent->_left) {
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
4.4 迭代器的不可修改性
红黑树的 key 是排序的依据,修改 key 会破坏树的结构,导致所有操作失效。所以我们需要保证:
set 的迭代器不能修改任何内容 :set 只存储 key,所以把红黑树的第二个模板参数设为const K,迭代器返回的就是const K&
map 的迭代器不能修改 key,但可以修改 value:map 存储pair<const K, V>,first是const K不能修改,second是V可以修改
cpp
// set的红黑树实例化:Value是const K,保证key不能修改
RBTree<K, const K, SetKeyOfT> _t;
// map的红黑树实例化:Value是pair<const K, V>,保证key不能修改
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
五、map 的 \[\] 运算符实现(最常用接口)
map的\[\]运算符是它最常用的接口,它的功能非常强大:
如果 key存在:返回对应 value 的引用,可以读取或修改
如果 key不存在:自动插入一个(key, 默认值)的键值对,然后返回默认值的引用
5.1 \[\] 运算符的实现原理
\[\]运算符的实现完全基于红黑树的Insert接口。Insert的返回值是pair<iterator, bool>:
iterator:指向插入的元素(或已存在的元素)的迭代器
bool:表示是否插入成功(true 表示新插入,false 表示已存在)
所以\[\]的实现逻辑非常简单:
调用Insert插入(key, V())(V 的默认构造值)
返回迭代器指向的 value 的引用
人话就是可以理解为插入到键值对是在iterator里面的二叉树结点指针封装,bool作用在表层体现
5.2 代码实现
cpp
tempalte<template K,typename V>
class map{
public:
//运算符
//重点研究!!!
V& operator[](const K& key)
{
//插入(key,默认值),如果key已存在则插入失败
pair<iterator, bool> ret = insert({ key, V() });
//返回value的引用
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
}
5.3 使用示例与注意事项
cpp
int main() {
map<string, int> freq;
// 统计词频:如果单词不存在,自动插入(单词, 0),然后+1
freq["hello"]++; // 插入("hello", 0),然后变成1
freq["world"]++; // 插入("world", 0),然后变成1
freq["hello"]++; // 已存在,直接+1,变成2
cout << freq["hello"] << endl; // 输出:2
cout << freq["world"] << endl; // 输出:1
// 注意!查找不要用[],会插入多余元素
// if (freq["c++"] == 0) {} // 错误!会插入("c++", 0)
// 正确做法:用find
if (freq.find("c++") != freq.end()) {
cout << freq["c++"] << endl;
}
return 0;
}
如果是插入或修改,用\[\]运算符,代码更简洁
如果只是查找,用find接口,避免插入多余的默认值
六、完整的红黑树实现
下面是整合了仿函数、迭代器、插入、查找的完整红黑树代码:
cpp
#pragma once
enum Colour
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
// 这里更新控制平衡也要加入parent指针
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
RBTreeNode(const T& data)
:_data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
Node* _root;
RBTreeIterator(Node* node, Node* root)
:_node(node)
,_root(root)
{}
Self operator++()
{
if (_node->_right)
{
// 右不为空,中序下一个访问的节点是右子树的最左(最小)节点
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
// 右为空,祖先里面孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Self operator--()
{
if (_node == nullptr) // --end()
{
// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点
Node* rightMost = _root;
while (rightMost && rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else if (_node->_left)
{
// 左子树不为空,中序左子树最后一个
Node* rightMost = _node->_left;
while (rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else
{
// 孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = 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
{
typedef RBTreeNode<T> Node;
public:
typedef RBTreeIterator<T, T&, T*> Iterator;
typedef RBTreeIterator<T, const T&, const T*> ConstIterator;
Iterator Begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur, _root);
}
Iterator End()
{
return Iterator(nullptr, _root);
}
ConstIterator Begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return ConstIterator(cur, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
pair<Iterator, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
//return pair<Iterator, bool>(Iterator(_root, _root), true);
return { Iterator(_root, _root), true };
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return { Iterator(cur, _root), false };
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 链接父亲
cur->_parent = parent;
// 父亲是红色,出现连续的红色节点,需要处理
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
// g
// p u
Node* uncle = grandfather->_right;
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 u
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
// g
// u p
Node* uncle = grandfather->_left;
// 叔叔存在且为红,-》变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者存在且为黑
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return { Iterator(newnode, _root), true };
}
void RotateR(Node * parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* pParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
{
pParent->_left = subL;
}
else
{
pParent->_right = subL;
}
subL->_parent = pParent;
}
}
void RotateL(Node * parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
private:
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;
}
int _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
private:
Node* _root = nullptr;
};
七、用红黑树封装 set 和 map
7.1 封装 set
cpp
#pragma once
#include"RBTree.h"
namespace bit
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;
iterator begin()
{
return _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);
}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
}
7.2map的封装
cpp
#pragma once
#include"RBTree.h"
namespace bit
{
template<class K, class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;
iterator begin()
{
return _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)
{
pair<iterator, bool> ret = insert({ key, V() });
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT> _t;
};
}
八、测试代码
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<time.h>
using namespace std;
//#include"RBTree.h"
//#include"AVLTree.h"
//void TestRBTree1()
//{
// RBTree<int, int> t;
// // 常规的测试用例
// //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
// // 特殊的带有双旋场景的测试用例
// int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
//
// for (auto e : a)
// {
// t.Insert({ e, e });
// }
//
// t.InOrder();
// cout << t.IsBalance() << endl;
//}
//
//void TestTree2()
//{
// const int N = 10000000;
// vector<int> v;
// v.reserve(N);
// srand(time(0));
// for (size_t i = 0; i < N; i++)
// {
// v.push_back(rand() + i);
// }
//
// size_t begin2 = clock();
// AVLTree<int, int> t;
// for (auto e : v)
// {
// t.Insert(make_pair(e, e));
// }
// size_t end2 = clock();
//
// size_t begin22 = clock();
// RBTree<int, int> rbt;
// for (auto e : v)
// {
// rbt.Insert(make_pair(e, e));
// }
// size_t end22 = clock();
//
// cout << "AVL Insert:" << end2 - begin2 << endl;
// cout << "RB Insert:" << end22 - begin22 << endl;
//
// cout <<"AVL IsBalance:"<< t.IsBalanceTree() << endl;
// cout << "RB IsBalance:" << rbt.IsBalance() << endl;
//
//
// cout << "AVL Height:" << t.Height() << endl;
// cout << "AVL Size:" << t.Size() << endl;
//
// cout << "RB Height:" << rbt.Height() << endl;
// cout << "RB Size:" << rbt.Size() << endl;
//
// size_t begin1 = clock();
// // 确定在的值
// for (auto e : v)
// {
// t.Find(e);
// }
// // 随机值
// /*for (size_t i = 0; i < N; i++)
// {
// t.Find((rand() + i));
// }*/
// size_t end1 = clock();
// cout << "AVL Find:" << end1 - begin1 << endl;
//
// size_t begin11 = clock();
// // 确定在的值
// for (auto e : v)
// {
// rbt.Find(e);
// }
// // 随机值
// /*for (size_t i = 0; i < N; i++)
// {
// t.Find((rand() + i));
// }*/
// size_t end11 = clock();
// cout << "RB Find:" << end11 - begin11 << endl;
//}
//
//int main()
//{
// TestTree2();
//
// return 0;
//}
#include"Myset.h"
#include"Mymap.h"
void Print(const bit::set<int>& s)
{
bit::set<int>::const_iterator it = s.end();
while (it != s.begin())
{
--it;
cout << *it << " ";
}
cout << endl;
}
int main()
{
bit::set<int> s;
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(6);
bit::set<int>::iterator sit = s.begin();
//*sit += 10;
while (sit != s.end())
{
cout << *sit << " ";
++sit;
}
cout << endl;
for (auto& e : s)
{
cout << e << " ";
}
cout << endl;
Print(s);
bit::map<string, string> dict;
dict.insert({ "sort", "排序" });
dict.insert({ "left", "左边" });
dict.insert({ "right", "右边" });
dict["left"] = "左边,剩余";
dict["insert"] = "插入";
dict["string"];
bit::map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
// 不能修改first,可以修改second
//it->first += 'x';
it->second += 'x';
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
for (auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
return 0;
}
九、核心设计思想总结
1、仿函数解耦 :用KeyOfT仿函数解耦比较逻辑和存储类型,让一颗红黑树同时支持 set 和 map
2、迭代器封装 :用迭代器封装中序遍历逻辑,让红黑树支持范围 for 和标准算法,++和--的实现基于局部节点判断,不需要全局信息
3、接口封装 :map的\[\]运算符基于Insert接口封装,提供了简洁的插入和修改体验
4、const 正确性 :通过模板参数加const,保证 set 的元素和 map 的 key 不能修改,维护红黑树的结构
这些设计思想让 STL 的红黑树具有极高的复用性、灵活性和安全性,是 C++ 泛型编程的经典范例。
红黑树是 STL 的核心底层,仿函数、迭代器和\[\]运算符是它能成为通用容器的关键。理解了这些设计,你就真正理解了 STL 的工程思想。
本文属于 《C++ 进阶篇系统教程》第 7 篇
关注我,第一时间收到更新,不用自己零散找资料,跟着系列系统学,少走 90% 的弯路!

