目录
[情况一: cur为红,p为红,g为黑,u存在且为红。](#情况一: cur为红,p为红,g为黑,u存在且为红。)
[情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑](#情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑)
[情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑](#情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑)
[红黑树模拟实现 STL 中的 map 与 set](#红黑树模拟实现 STL 中的 map 与 set)
[operator++ 的实现思路](#operator++ 的实现思路)
[operator-- 的实现思路](#operator-- 的实现思路)
[map 的模拟实现](#map 的模拟实现)
[map 的类定义](#map 的类定义)
[set 的模拟实现](#set 的模拟实现)
[set 的类定义](#set 的类定义)
底层原理
map/set等关联式容器的底层通常用平衡二叉搜索树(如红黑树)实现,而非普通的二叉搜索树。这是因为若使用普通二叉搜索树,当插入有序数据时,树会退化成近似单链的结构,使操作时间复杂度上升为O(N)。平衡树通过特定的旋转规则维持树的平衡,从而保证了操作的高效性(O(log N))。
红黑树
红黑树,是一种二叉搜索树 ,但在每个结点上增加一个存储位表示结点的颜色 ,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的。

性质:
-
每个结点不是红色就是黑色:利用这两种颜色去限制树形结构。
-
根节点是黑色的
-
如果一个节点是红色的,则它的两个孩子结点是黑色的
-
对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
-
每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点 个数的两倍?
答:最短路径,尽量全是黑节点;最长路径,黑节点之间夹红节点;又因为所有路径要求黑节点数必须一致,所以出现了这种情况:最短路径全为黑节点,最长路径两黑一红,所以最长路径就是最短路径的两倍。
结点的定义;
cpp
enum Color
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data = T(), Color color = RED)
: _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
{}
RBTreeNode<T>* _left; // 左孩子
RBTreeNode<T>* _right; // 右孩子
RBTreeNode<T>* _parent; // 父节点
T _data; // 节点中存储的数据
Color _color; // 节点颜色
};
**在节点的定义中,为什么要将节点的默认颜色给成红色?:**插红只可能破坏"双红",插黑可能破坏"整条路径黑高"
如果插入黑色,会更容易破坏黑高 假设你插入一个黑节点,那么它所在路径上的黑节点数立刻 +1。这就很容易破坏性质 :所有路径黑节点数相同。
一旦黑高不一致,调整起来很麻烦,影响会一路向上。
如果插入红色,通常不影响黑高
因为红节点不计入黑节点数量,所以插入红节点后:黑高通常没变;唯一可能破坏的是性质 3:父子不能同红。而"双红冲突"只需要局部修正,成本更低。
红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了 与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft 域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:

-
head->_parent指向根 -
head->_left指向最小节点 -
head->_right指向最大节点
红黑树插入
红黑树插入分两步
第一步仍然是普通二叉搜索树插入
第二步是插入后的颜色修正
这里最重要的是:
新节点默认是红色
如果父节点是黑色,不会破坏红黑树性质,直接结束
如果父节点也是红色,就违反了"不能有连续红色节点"这一条性质,这时必须调整
调整时分三大类
- 叔叔存在且为红,变色并继续向上处理
- 叔叔不存在或为黑,并且当前节点和父节点、祖父节点在一条直线上,单旋加变色
- 叔叔不存在或为黑,并且当前节点和父节点、祖父节点形成折线,先旋成直线,再按上一种处理
cpp
public:
std::pair<Iterator, bool> Insert(const ValueType& data)
{
KeyOfValue keyOfValue;
Node*& root = _GetRoot();
// 如果树为空,直接插入根节点
// 根节点必须为黑色
if (root == nullptr)
{
root = new Node(data, BLACK);
root->_parent = _head;
_head->_left = root;
_head->_right = root;
++_size;
return std::make_pair(Iterator(root), true);
}
// 按照二叉搜索树规则查找插入位置
Node* parent = root;
Node* cur = root;
K key = keyOfValue(data);
while (cur)
{
parent = cur;
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
return std::make_pair(Iterator(cur), false);
}
}
// 创建新节点
// 红黑树中新插入节点默认给红色
Node* newNode = new Node(data, RED);
newNode->_parent = parent;
if (key < keyOfValue(parent->_data))
{
parent->_left = newNode;
}
else
{
parent->_right = newNode;
}
// 更新最小节点和最大节点
if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
{
_head->_left = newNode;
}
if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
{
_head->_right = newNode;
}
++_size;
// 从新节点开始向上检查是否出现连续红色节点
cur = newNode;
parent = cur->_parent;
while (parent != _head && parent->_color == RED)
{
// 既然父节点是红色,那么祖父节点一定存在
Node* grandfather = parent->_parent;
// 先处理父节点在祖父左边的情况
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 叔叔存在且为红色
// 这种情况只需要变色,然后把祖父当成新的当前节点继续向上处理
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
// 如果当前节点在父节点右边
// 说明形成了折线,先对父节点做左旋,把它转成直线
if (cur == parent->_right)
{
_RotateL(parent);
std::swap(cur, parent);
}
// 走到这里,一定变成了直线情况
// 父节点染黑,祖父节点染红,然后对祖父做右旋
parent->_color = BLACK;
grandfather->_color = RED;
_RotateR(grandfather);
break;
}
}
else
{
// 父节点在祖父右边时,处理逻辑完全对称
Node* uncle = grandfather->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
_RotateR(parent);
std::swap(cur, parent);
}
parent->_color = BLACK;
grandfather->_color = RED;
_RotateL(grandfather);
break;
}
}
}
// 无论中间怎么调整,根节点最后都必须重新染成黑色
root->_color = BLACK;
root->_parent = _head;
return std::make_pair(Iterator(newNode), true);
}
检测新节点插入后,红黑树的性质是否造到破坏,若满足直接退出,否则对红黑树进行旋转着色处理
新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;
但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连 在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
情况一: cur为红,p为红,g为黑,u存在且为红。

cur和p均为红,违反了性质三,此处能否将p直接改为黑?
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色--p变黑,g变红
情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2

红黑树的验证
红黑树的检测分为两步:
-
检测其是否满足二叉搜索树(中序遍历是否为有序序列)
-
检测其是否满足红黑树的性质
这里我把他整理成了公开接口加私有递归函数两层
IsValidRBTree
cpp
bool IsValidRBTree() const
{
Node* root = _head->_parent;
// 空树也是红黑树
if (root == nullptr)
{
return true;
}
// 根节点必须为黑色
if (root->_color != BLACK)
{
return false;
}
// 先取最左路径上的黑色节点个数作为基准
size_t blackCount = 0;
Node* cur = root;
while (cur)
{
if (cur->_color == BLACK)
{
++blackCount;
}
cur = cur->_left;
}
size_t pathBlack = 0;
return _IsValidRBTree(root, pathBlack, blackCount);
}
_IsValidRBTree
这个递归函数主要检查两件事
是否存在连续红色节点
每条路径上的黑色节点个数是否相同
cpp
private:
bool _IsValidRBTree(Node* root, size_t pathBlack, const size_t blackCount) const
{
// 走到空节点时,检查当前路径上的黑色节点数是否和基准一致
if (root == nullptr)
{
return pathBlack == blackCount;
}
// 当前节点如果是黑色,就把黑色计数加 1
if (root->_color == BLACK)
{
++pathBlack;
}
// 如果当前节点和父节点都为红色,就违反了性质
if (root->_parent != _head &&
root->_parent->_color == RED &&
root->_color == RED)
{
return false;
}
return _IsValidRBTree(root->_left, pathBlack, blackCount) &&
_IsValidRBTree(root->_right, pathBlack, blackCount);
}
红黑树整体结构与头结点
红黑树这里和普通二叉搜索树最大的不同,不只是颜色,还有一个专门的头结点 _head
这个头结点不是有效数据节点,它主要有三个作用:
_head->_parent指向根节点_head->_left指向整棵树中最小的节点_head->_right指向整棵树中最大的节点
这样设计以后有几个明显好处:
- 获取根节点更方便
begin()可以直接返回最小节点end()可以直接放在_head- 迭代器做
--end()时,可以直接回到最大节点
所以头结点其实是为了后面实现迭代器和模拟 map/set 做准备的
cpp
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
using Node = RBTreeNode<ValueType>;
public:
using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;
RBTree()
: _size(0)
{
// 创建头结点
_head = new Node;
// 头结点颜色给红色,主要是为了和普通根节点区分
_head->_color = RED;
// 头结点初始化时还没有根节点
_head->_parent = nullptr;
// 空树时,最小节点和最大节点都先指向头结点自己
_head->_left = _head;
_head->_right = _head;
}
private:
// 头结点的 parent 域就是根节点
Node*& _GetRoot()
{
return _head->_parent;
}
private:
Node* _head;
size_t _size;
};
左旋
左旋处理的是"右边上升"的结构变化
它本质上做了这几件事:
- 让
parent的右孩子subR上升 - 让
parent下沉到subR的左边 - 把
subR原来的左子树接到parent的右边 - 最后把新子树根重新接回原来的祖先节点
左旋和右旋是红黑树插入调整的基础操作,插入修复时不管是直线型还是折线型,最后都要落到这两个旋转上
cpp
void _RotateL(Node* parent)
{
// 1. 先记录 parent 的右孩子
Node* subR = parent->_right;
// 2. 再记录 subR 的左子树
// 旋转后,这棵子树要接到 parent 的右边
Node* subRL = subR->_left;
// 3. 保存 parent 原来的父节点
Node* ppNode = parent->_parent;
// 4. 让 parent 接住 subR 的左子树
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
// 5. 让 subR 上升,parent 下沉到 subR 左边
subR->_left = parent;
subR->_parent = ppNode;
parent->_parent = subR;
// 6. 把旋转后的新子树根接回原来的位置
if (ppNode == _head)
{
_head->_parent = subR;
}
else if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
}
右旋
右旋和左旋完全对称
它本质上做的是:
- 让
parent的左孩子subL上升 - 让
parent下沉到subL的右边 - 把
subL原来的右子树接到parent的左边 - 最后把新子树根重新接回原来的祖先节点
cpp
void _RotateR(Node* parent)
{
// 1. 先记录 parent 的左孩子
Node* subL = parent->_left;
// 2. 再记录 subL 的右子树
// 旋转后,这棵子树要接到 parent 的左边
Node* subLR = subL->_right;
// 3. 保存 parent 原来的父节点
Node* ppNode = parent->_parent;
// 4. 让 parent 接住 subL 的右子树
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
// 5. 让 subL 上升,parent 下沉到 subL 右边
subL->_right = parent;
subL->_parent = ppNode;
parent->_parent = subL;
// 6. 把旋转后的新子树根接回原来的位置
if (ppNode == _head)
{
_head->_parent = subL;
}
else if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
}
迭代器自增
迭代器 ++ 的本质,是找当前节点的中序后继
分两种情况:
-
当前节点右子树存在
后继就是右子树中最左侧的节点
-
当前节点右子树不存在
就沿着父节点一路往上找,直到找到一个祖先,使得当前节点位于这个祖先的左边,那么这个祖先就是后继
cpp
void _Increasement()
{
// 如果右子树存在
// 后继一定在右子树中,并且是右子树里最左侧的节点
if (_node->_right)
{
_node = _node->_right;
while (_node->_left)
{
_node = _node->_left;
}
}
else
{
// 如果右子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的右孩子
Node* parent = _node->_parent;
while (parent->_right == _node)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是当前节点的后继
if (_node->_right != parent)
{
_node = parent;
}
}
}
迭代器自减
迭代器 -- 的本质,是找当前节点的中序前驱
这里比自增多一个特殊情况:
- 如果当前节点在
_head,说明它是end()
那么--end()应该直接回到最大节点
其余情况和找前驱的常规规则一样:
- 左子树存在,就去左子树找最右节点
- 左子树不存在,就向上找第一个把当前节点放在右边的祖先
cpp
void _Decreasement()
{
// 如果当前在头结点位置
// 说明此时是 end(),做 -- 应该回到最大节点
if (_node->_parent->_parent == _node && _node->_color == RED)
{
_node = _node->_right;
}
else if (_node->_left)
{
// 如果左子树存在
// 前驱就是左子树中最右侧的节点
_node = _node->_left;
while (_node->_right)
{
_node = _node->_right;
}
}
else
{
// 如果左子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的左孩子
Node* parent = _node->_parent;
while (_node == parent->_left)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是前驱
_node = parent;
}
}
Find
查找操作仍然遵循二叉搜索树规则
但这里不能直接比较整个 ValueType,而是要先通过 KeyOfValue 取出 key
这一点很重要,因为后面同一棵红黑树模板还要服务 map 和 set
set比较的是值本身map比较的是pair里的first
cpp
Iterator Find(const K& key)
{
KeyOfValue keyOfValue;
Node* cur = _GetRoot();
// 按照二叉搜索树规则查找
while (cur)
{
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
return Iterator(cur);
}
}
// 没找到就返回 end()
return End();
}
Clear
清空整棵树时,使用后序遍历最稳妥
因为必须先删左右子树,再删当前节点,否则一旦先删当前节点,下面的孩子就无法继续访问
同时删除完成后,还要把头结点恢复成初始状态
cpp
void Clear()
{
// 先递归释放整棵树
_Destroy(_GetRoot());
// 清空后,根节点重新置空
_head->_parent = nullptr;
// 最小节点和最大节点重新指向头结点自己
_head->_left = _head;
_head->_right = _head;
// 有效节点个数清零
_size = 0;
}
void _Destroy(Node* root)
{
// 空节点直接返回
if (root == nullptr)
{
return;
}
// 先释放左子树
_Destroy(root->_left);
// 再释放右子树
_Destroy(root->_right);
// 最后释放当前节点
delete root;
}
代码(总)
cpp
#include <iostream>
#include <utility>
enum Color
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data = T(), Color color = RED)
: _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
{}
RBTreeNode<T>* _left; // 左孩子
RBTreeNode<T>* _right; // 右孩子
RBTreeNode<T>* _parent; // 父节点
T _data; // 数据域
Color _color; // 颜色
};
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
using Node = RBTreeNode<T>;
using Self = RBTreeIterator<T, Ref, Ptr>;
RBTreeIterator(Node* node = nullptr)
: _node(node)
{}
Ref operator*() const
{
return _node->_data;
}
Ptr operator->() const
{
return &_node->_data;
}
bool operator!=(const Self& it) const
{
return _node != it._node;
}
bool operator==(const Self& it) const
{
return _node == it._node;
}
Self& operator++()
{
_Increasement();
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_Increasement();
return tmp;
}
Self& operator--()
{
_Decreasement();
return *this;
}
Self operator--(int)
{
Self tmp(*this);
_Decreasement();
return tmp;
}
void _Increasement()
{
// 如果右子树存在
// 后继就是右子树中最左侧的节点
if (_node->_right)
{
_node = _node->_right;
while (_node->_left)
{
_node = _node->_left;
}
}
else
{
// 如果右子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的右孩子
Node* parent = _node->_parent;
while (parent->_right == _node)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是后继
if (_node->_right != parent)
{
_node = parent;
}
}
}
void _Decreasement()
{
// 如果当前在头结点位置
// 说明当前是 end(),做 -- 应该回到最大节点
if (_node->_parent->_parent == _node && _node->_color == RED)
{
_node = _node->_right;
}
else if (_node->_left)
{
// 如果左子树存在
// 前驱就是左子树中最右侧的节点
_node = _node->_left;
while (_node->_right)
{
_node = _node->_right;
}
}
else
{
// 如果左子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的左孩子
Node* parent = _node->_parent;
while (_node == parent->_left)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是前驱
_node = parent;
}
}
Node* _node;
};
template<class T>
struct Identity
{
const T& operator()(const T& value) const
{
return value;
}
};
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
using Node = RBTreeNode<ValueType>;
public:
using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;
RBTree()
: _size(0)
{
// 创建头结点
_head = new Node;
// 头结点给红色,主要是为了和普通根节点区分
_head->_color = RED;
// 初始时没有根节点
_head->_parent = nullptr;
// 空树时,最小节点和最大节点都指向头结点自己
_head->_left = _head;
_head->_right = _head;
}
~RBTree()
{
Clear();
delete _head;
_head = nullptr;
}
RBTree(const RBTree&) = delete;
RBTree& operator=(const RBTree&) = delete;
public:
Iterator Begin()
{
// begin() 放在最小节点位置
return Iterator(_head->_left);
}
Iterator End()
{
// end() 放在头结点位置
return Iterator(_head);
}
std::pair<Iterator, bool> Insert(const ValueType& data)
{
KeyOfValue keyOfValue;
Node*& root = _GetRoot();
// 如果当前树为空,直接插入根节点
// 根节点必须是黑色
if (root == nullptr)
{
root = new Node(data, BLACK);
root->_parent = _head;
_head->_left = root;
_head->_right = root;
++_size;
return std::make_pair(Iterator(root), true);
}
// 按照二叉搜索树规则查找插入位置
Node* parent = root;
Node* cur = root;
K key = keyOfValue(data);
while (cur)
{
parent = cur;
// 如果当前 key 更小,就继续往左走
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
// 如果当前 key 更大,就继续往右走
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
// 如果 key 已经存在,就不再插入
else
{
return std::make_pair(Iterator(cur), false);
}
}
// 创建新节点
// 红黑树中新插入节点默认给红色
Node* newNode = new Node(data, RED);
newNode->_parent = parent;
// 把新节点挂到父节点下面
if (key < keyOfValue(parent->_data))
{
parent->_left = newNode;
}
else
{
parent->_right = newNode;
}
// 如果新节点更小,就更新最小节点
if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
{
_head->_left = newNode;
}
// 如果新节点更大,就更新最大节点
if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
{
_head->_right = newNode;
}
++_size;
// 插入后如果父节点也是红色
// 就违反了不能有连续红色节点的性质
cur = newNode;
parent = cur->_parent;
while (parent != _head && parent->_color == RED)
{
// 既然父节点是红色,那么祖父节点一定存在
Node* grandfather = parent->_parent;
// 先处理父节点在祖父左边的情况
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 叔叔存在并且为红色
// 只需要变色,然后继续向上处理
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
// 如果当前节点在父节点右边
// 说明是折线结构,先左旋父节点,转成直线结构
if (cur == parent->_right)
{
_RotateL(parent);
std::swap(cur, parent);
}
// 走到这里一定是直线结构
// 父节点染黑,祖父节点染红,然后右旋祖父节点
parent->_color = BLACK;
grandfather->_color = RED;
_RotateR(grandfather);
break;
}
}
else
{
// 父节点在祖父右边时,处理逻辑和上面对称
Node* uncle = grandfather->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
_RotateR(parent);
std::swap(cur, parent);
}
parent->_color = BLACK;
grandfather->_color = RED;
_RotateL(grandfather);
break;
}
}
}
// 无论中间怎么调整,根节点最后都必须是黑色
root->_color = BLACK;
root->_parent = _head;
return std::make_pair(Iterator(newNode), true);
}
Iterator Find(const K& key)
{
KeyOfValue keyOfValue;
Node* cur = _GetRoot();
// 按照二叉搜索树规则查找
while (cur)
{
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
return Iterator(cur);
}
}
// 没找到返回 end()
return End();
}
void Clear()
{
// 先递归释放整棵树
_Destroy(_GetRoot());
// 清空后恢复头结点状态
_head->_parent = nullptr;
_head->_left = _head;
_head->_right = _head;
_size = 0;
}
size_t Size() const
{
return _size;
}
bool Empty() const
{
return _size == 0;
}
bool IsValidRBTree() const
{
Node* root = _head->_parent;
// 空树也是红黑树
if (root == nullptr)
{
return true;
}
// 根节点必须为黑色
if (root->_color != BLACK)
{
return false;
}
// 取最左路径上的黑色节点个数作为基准
size_t blackCount = 0;
Node* cur = root;
while (cur)
{
if (cur->_color == BLACK)
{
++blackCount;
}
cur = cur->_left;
}
size_t pathBlack = 0;
return _IsValidRBTree(root, pathBlack, blackCount);
}
private:
void _RotateL(Node* parent)
{
// 记录右孩子和右孩子的左子树
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* ppNode = parent->_parent;
// 让 parent 接住 subR 的左子树
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
// 让 subR 上升,parent 下沉到左边
subR->_left = parent;
subR->_parent = ppNode;
parent->_parent = subR;
// 把新的子树根重新接回原来的位置
if (ppNode == _head)
{
_head->_parent = subR;
}
else if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
}
void _RotateR(Node* parent)
{
// 记录左孩子和左孩子的右子树
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* ppNode = parent->_parent;
// 让 parent 接住 subL 的右子树
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
// 让 subL 上升,parent 下沉到右边
subL->_right = parent;
subL->_parent = ppNode;
parent->_parent = subL;
// 把新的子树根重新接回原来的位置
if (ppNode == _head)
{
_head->_parent = subL;
}
else if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
}
bool _IsValidRBTree(Node* root, size_t pathBlack, const size_t blackCount) const
{
// 走到空节点时,检查黑色节点个数是否一致
if (root == nullptr)
{
return pathBlack == blackCount;
}
// 当前节点如果是黑色,就计入当前路径黑节点个数
if (root->_color == BLACK)
{
++pathBlack;
}
// 如果当前节点和父节点都为红色,就违反性质
if (root->_parent != _head &&
root->_parent->_color == RED &&
root->_color == RED)
{
return false;
}
return _IsValidRBTree(root->_left, pathBlack, blackCount) &&
_IsValidRBTree(root->_right, pathBlack, blackCount);
}
void _Destroy(Node* root)
{
// 空节点直接返回
if (root == nullptr)
{
return;
}
// 先释放左子树
_Destroy(root->_left);
// 再释放右子树
_Destroy(root->_right);
// 最后释放当前节点
delete root;
}
Node*& _GetRoot()
{
// 头结点的 parent 域就是根节点
return _head->_parent;
}
private:
Node* _head; // 头结点
size_t _size; // 有效节点个数
};
int main()
{
RBTree<int, int, Identity<int>> t;
int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
for (int x : arr)
{
t.Insert(x);
}
std::cout << t.IsValidRBTree() << std::endl; // 1
std::cout << t.Size() << std::endl; // 9
for (auto it = t.Begin(); it != t.End(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
auto pos = t.Find(14);
if (pos != t.End())
{
std::cout << *pos << std::endl; // 14
}
auto last = t.End();
--last;
std::cout << *last << std::endl; // 26
t.Clear();
std::cout << t.Empty() << std::endl; // 1
return 0;
}
红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log N),红黑树不追 求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数, 所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红 黑树更多。
红黑树的应用
-
C++ STL库 -- map/set、mutil_map/mutil_set
-
Java 库
-
linux内核
-
其他一些库
红黑树模拟实现STL中的map与set
红黑树的迭代器
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后, 可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位 置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块? 能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最 后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

红黑树模拟实现 STL 中的 map 与 set
这一部分的核心目标,不是重新写一套新的平衡树,而是把前面已经实现好的红黑树继续抽象,让它能够同时作为 map 和 set 的底层结构
这里最关键的矛盾是:
set中存的是keymap中存的是pair<K, V>
但是这两个容器底层都希望复用同一棵红黑树
所以红黑树必须继续改造,不能再简单写成"存什么就比较什么",而要改成:
- 节点里存的是
ValueType - 真正参与比较的是
key key如何从ValueType中取出来,由KeyOfValue决定
红黑树的迭代器
这一部分主要解决两个问题:
begin()和end()应该放在哪里- 迭代器的
++和--应该如何移动
红黑树本质上还是二叉搜索树,所以对它做中序遍历,得到的一定是一个有序序列
因此:
begin()应该放在最小节点,也就是最左侧节点end()不能简单放成nullptr- 最适合把
end()放在头结点_head
为什么 end() 不能给成 nullptr?因为 STL 中允许对 end() 做 --,也就是要求 --end() 能回到最后一个有效元素,如果 end() 是空指针,就无法从空指针再找到最大节点
所以才会专门给红黑树增加一个头结点 _head
_head->_parent指向根节点_head->_left指向最小节点_head->_right指向最大节点
这样:
begin()就能直接返回最小节点end()就能直接返回头结点--end()就能直接回到最大节点
迭代器定义
cpp
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
using Node = RBTreeNode<T>;
using Self = RBTreeIterator<T, Ref, Ptr>;
RBTreeIterator(Node* node = nullptr)
: _node(node)
{}
Ref operator*() const
{
return _node->_data;
}
Ptr operator->() const
{
return &_node->_data;
}
bool operator!=(const Self& it) const
{
return _node != it._node;
}
bool operator==(const Self& it) const
{
return _node == it._node;
}
Self& operator++()
{
_Increasement();
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_Increasement();
return tmp;
}
Self& operator--()
{
_Decreasement();
return *this;
}
Self operator--(int)
{
Self tmp(*this);
_Decreasement();
return tmp;
}
Node* _node;
};
operator++ 的实现思路
迭代器自增,本质上是在找当前节点的中序后继
分两种情况:
-
当前节点右子树存在
后继就是右子树中最左侧的节点
-
当前节点右子树不存在
就沿着父节点向上找,直到找到一个祖先,使当前节点处于它的左侧,那么这个祖先就是后继
_Increasement
cpp
void _Increasement()
{
// 如果右子树存在
// 后继一定在右子树中,并且是右子树里最左侧的节点
if (_node->_right)
{
_node = _node->_right;
while (_node->_left)
{
_node = _node->_left;
}
}
else
{
// 如果右子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的右孩子
Node* parent = _node->_parent;
while (parent->_right == _node)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是当前节点的后继
if (_node->_right != parent)
{
_node = parent;
}
}
}
operator-- 的实现思路
迭代器自减,本质上是在找当前节点的中序前驱
分三种情况:
-
当前节点在头结点位置
说明当前是
end(),此时--应该直接回到最大节点 -
当前节点左子树存在
前驱就是左子树中最右侧的节点
-
当前节点左子树不存在
就沿着父节点向上找,直到找到一个祖先,使当前节点处于它的右侧,那么这个祖先就是前驱
_Decreasement
cpp
void _Decreasement()
{
// 如果当前在头结点位置
// 说明当前是 end(),做 -- 应该回到整棵树中最大的节点
if (_node->_parent->_parent == _node && _node->_color == RED)
{
_node = _node->_right;
}
else if (_node->_left)
{
// 如果左子树存在
// 前驱就是左子树中最右侧的节点
_node = _node->_left;
while (_node->_right)
{
_node = _node->_right;
}
}
else
{
// 如果左子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的左孩子
Node* parent = _node->_parent;
while (_node == parent->_left)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是当前节点的前驱
_node = parent;
}
}
改造红黑树
这一部分的重点,是把前面"只能存一种数据"的红黑树,改造成能服务关联式容器的通用红黑树
思路:
K表示真正的 key 类型ValueType表示节点中真正存储的数据类型KeyOfValue表示一个仿函数,用来从ValueType中取出 key
这样设计之后:
- 对
set,ValueType就是K - 对
map,ValueType就是pair<K, V>
红黑树不再关心你节点里到底存的是 K 还是 pair<K, V>
它只关心一件事:
如何从节点保存的数据中取出 key,并用 key 来比较
改造后的红黑树模板
cpp
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
using Node = RBTreeNode<ValueType>;
public:
using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;
RBTree()
: _size(0)
{
// 创建头结点
_head = new Node;
// 头结点给红色,主要用于和普通节点区分
_head->_color = RED;
// 初始时还没有根节点
_head->_parent = nullptr;
// 空树时,最小节点和最大节点都先指向头结点自己
_head->_left = _head;
_head->_right = _head;
}
~RBTree()
{
Clear();
delete _head;
_head = nullptr;
}
private:
Node* _head; // 头结点
size_t _size; // 有效节点个数
};
Begin
Begin() 返回的是最小节点位置
cpp
Iterator Begin()
{
// begin() 指向整棵树中最小的节点
return Iterator(_head->_left);
}
End
End() 返回的是头结点位置
cpp
Iterator End()
{
// end() 放在头结点位置
return Iterator(_head);
}
Insert
比较时不能直接比较整个 ValueType,必须先通过 KeyOfValue 取出 key 再比较
这正是"支持 map/set 共用一套底层"的关键
cpp
std::pair<Iterator, bool> Insert(const ValueType& data)
{
KeyOfValue keyOfValue;
Node*& root = _GetRoot();
// 如果树为空,直接插入根节点
if (root == nullptr)
{
root = new Node(data, BLACK);
root->_parent = _head;
_head->_left = root;
_head->_right = root;
++_size;
return std::make_pair(Iterator(root), true);
}
// 先取出待插入数据对应的 key
K key = keyOfValue(data);
// 按照二叉搜索树规则查找插入位置
Node* parent = root;
Node* cur = root;
while (cur)
{
parent = cur;
// 比较时比较的是 key,不是整个 ValueType
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
// key 相同,说明元素已存在
return std::make_pair(Iterator(cur), false);
}
}
// 创建新节点,新节点默认给红色
Node* newNode = new Node(data, RED);
newNode->_parent = parent;
// 把新节点挂到父节点下面
if (key < keyOfValue(parent->_data))
{
parent->_left = newNode;
}
else
{
parent->_right = newNode;
}
// 如果新节点更小,就更新最小节点
if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
{
_head->_left = newNode;
}
// 如果新节点更大,就更新最大节点
if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
{
_head->_right = newNode;
}
++_size;
// 下面这部分仍然是红黑树插入调整
// 如果父节点也是红色,就违反了不能有连续红色节点的性质
cur = newNode;
parent = cur->_parent;
while (parent != _head && parent->_color == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 叔叔存在并且为红色
// 这种情况只需要变色,然后继续向上处理祖父节点
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
// 如果当前节点在父节点右边
// 说明是折线结构,先左旋父节点,把它转成直线结构
if (cur == parent->_right)
{
_RotateL(parent);
std::swap(cur, parent);
}
// 走到这里一定是直线结构
// 父节点染黑,祖父节点染红,然后右旋祖父节点
parent->_color = BLACK;
grandfather->_color = RED;
_RotateR(grandfather);
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
_RotateR(parent);
std::swap(cur, parent);
}
parent->_color = BLACK;
grandfather->_color = RED;
_RotateL(grandfather);
break;
}
}
}
// 根节点最后必须保持黑色
root->_color = BLACK;
root->_parent = _head;
return std::make_pair(Iterator(newNode), true);
}
Find
查找和插入的查找过程一样,关键也在于:先取 key,再比较
cpp
Iterator Find(const K& key)
{
KeyOfValue keyOfValue;
Node* cur = _GetRoot();
// 按照二叉搜索树规则查找
while (cur)
{
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
return Iterator(cur);
}
}
// 没找到就返回 end()
return End();
}
map 的模拟实现
思想:
map 的底层本来就是红黑树,所以只需要在 map 内部封装一棵合适类型的红黑树,再把接口简单包装一下即可
这里节点里真正存储的是:
cpp
pair<K, V>
但比较大小时,比较的是 pair.first
所以这里需要定义一个专门给 map 使用的 KeyOfValue
MapKeyOfValue
cpp
template<class K, class V>
struct MapKeyOfValue
{
const K& operator()(const std::pair<K, V>& value) const
{
// map 比较大小时,只看键值对中的 first
return value.first;
}
};
map 的类定义
cpp
template<class K, class V>
class map
{
using ValueType = std::pair<K, V>;
using RBTreeType = RBTree<K, ValueType, MapKeyOfValue<K, V>>;
public:
using iterator = typename RBTreeType::Iterator;
private:
RBTreeType _tree;
};
begin
cpp
iterator begin()
{
// 直接调用底层红黑树的 Begin
return _tree.Begin();
}
end
cpp
iterator end()
{
// 直接调用底层红黑树的 End
return _tree.End();
}
insert
map 插入的本质,就是向底层红黑树插入一个键值对
cpp
std::pair<iterator, bool> insert(const ValueType& data)
{
// 直接把键值对交给底层红黑树插入
return _tree.Insert(data);
}
find
查找时只需要传入 key
cpp
iterator find(const K& key)
{
// 底层红黑树支持按 key 查找
return _tree.Find(key);
}
operator[]
这是 map 最有代表性的接口,也是面试和源码分析里最常问的地方
它的行为是:
- 如果 key 已存在,就返回该节点中的
second - 如果 key 不存在,就先插入一个
pair(key, V()) - 然后再返回这个节点中的
second
所以它实际上是"插入 + 定位 + 取值"的组合
cpp
V& operator[](const K& key)
{
// 如果 key 不存在,会插入 pair(key, V())
// 如果 key 已存在,会直接返回原来的位置
// 无论哪种情况,Insert 返回值里的 first 都是对应节点的迭代器
// 对这个迭代器解引用后得到 pair<K, V>
// 再取 second,就拿到了 value
return (*(_tree.Insert(ValueType(key, V())).first)).second;
}
其他辅助接口
cpp
void clear()
{
_tree.Clear();
}
size_t size() const
{
return _tree.Size();
}
bool empty() const
{
return _tree.Empty();
}
set 的模拟实现
set 比 map 更简单,因为它保存的就是 key 本身
所以:
ValueType就是KKeyOfValue直接把参数本身返回即可
这里通过 set 再次说明:
同一棵红黑树模板,只要换掉 ValueType 和 KeyOfValue,就能支持不同容器
SetKeyOfValue
cpp
template<class K>
struct SetKeyOfValue
{
const K& operator()(const K& key) const
{
// set 中存的就是 key 本身
// 直接返回即可
return key;
}
};
set 的类定义
cpp
template<class K>
class set
{
using ValueType = K;
using RBTreeType = RBTree<K, ValueType, SetKeyOfValue<K>>;
public:
using iterator = typename RBTreeType::Iterator;
private:
RBTreeType _tree;
};
begin
cpp
iterator begin()
{
// 直接调用底层红黑树的 Begin
return _tree.Begin();
}
end
cpp
iterator end()
{
// 直接调用底层红黑树的 End
return _tree.End();
}
insert
set 的插入,本质上就是向底层红黑树插入一个 key
cpp
std::pair<iterator, bool> insert(const ValueType& data)
{
// 直接把元素交给底层红黑树插入
return _tree.Insert(data);
}
find
cpp
iterator find(const K& key)
{
// 直接调用底层红黑树查找
return _tree.Find(key);
}
其他辅助接口
cpp
void clear()
{
_tree.Clear();
}
size_t size() const
{
return _tree.Size();
}
bool empty() const
{
return _tree.Empty();
}
完整代码
cpp
#include <iostream>
#include <string>
#include <utility>
enum Color
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data = T(), Color color = RED)
: _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
{}
RBTreeNode<T>* _left; // 左孩子
RBTreeNode<T>* _right; // 右孩子
RBTreeNode<T>* _parent; // 父节点
T _data; // 数据域
Color _color; // 颜色
};
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
using Node = RBTreeNode<T>;
using Self = RBTreeIterator<T, Ref, Ptr>;
RBTreeIterator(Node* node = nullptr)
: _node(node)
{}
Ref operator*() const
{
return _node->_data;
}
Ptr operator->() const
{
return &_node->_data;
}
bool operator!=(const Self& it) const
{
return _node != it._node;
}
bool operator==(const Self& it) const
{
return _node == it._node;
}
Self& operator++()
{
_Increasement();
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_Increasement();
return tmp;
}
Self& operator--()
{
_Decreasement();
return *this;
}
Self operator--(int)
{
Self tmp(*this);
_Decreasement();
return tmp;
}
void _Increasement()
{
// 如果右子树存在
// 后继一定在右子树中,并且是右子树里最左侧的节点
if (_node->_right)
{
_node = _node->_right;
while (_node->_left)
{
_node = _node->_left;
}
}
else
{
// 如果右子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的右孩子
Node* parent = _node->_parent;
while (parent->_right == _node)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是当前节点的后继
if (_node->_right != parent)
{
_node = parent;
}
}
}
void _Decreasement()
{
// 如果当前在头结点位置
// 说明当前是 end(),做 -- 应该回到整棵树中最大的节点
if (_node->_parent->_parent == _node && _node->_color == RED)
{
_node = _node->_right;
}
else if (_node->_left)
{
// 如果左子树存在
// 前驱就是左子树中最右侧的节点
_node = _node->_left;
while (_node->_right)
{
_node = _node->_right;
}
}
else
{
// 如果左子树不存在
// 就沿着父节点向上找,直到当前节点不是父节点的左孩子
Node* parent = _node->_parent;
while (_node == parent->_left)
{
_node = parent;
parent = parent->_parent;
}
// 找到以后,这个父节点就是当前节点的前驱
_node = parent;
}
}
Node* _node;
};
template<class K, class ValueType, class KeyOfValue>
class RBTree
{
using Node = RBTreeNode<ValueType>;
public:
using Iterator = RBTreeIterator<ValueType, ValueType&, ValueType*>;
RBTree()
: _size(0)
{
// 创建头结点
_head = new Node;
// 头结点颜色给红色,主要用于和普通节点区分
_head->_color = RED;
// 初始时还没有根节点
_head->_parent = nullptr;
// 空树时,最小节点和最大节点都先指向头结点自己
_head->_left = _head;
_head->_right = _head;
}
~RBTree()
{
Clear();
delete _head;
_head = nullptr;
}
RBTree(const RBTree&) = delete;
RBTree& operator=(const RBTree&) = delete;
public:
Iterator Begin()
{
// begin() 指向最小节点
return Iterator(_head->_left);
}
Iterator End()
{
// end() 放在头结点位置
return Iterator(_head);
}
std::pair<Iterator, bool> Insert(const ValueType& data)
{
KeyOfValue keyOfValue;
Node*& root = _GetRoot();
// 如果当前树为空,直接插入根节点
// 根节点必须为黑色
if (root == nullptr)
{
root = new Node(data, BLACK);
root->_parent = _head;
_head->_left = root;
_head->_right = root;
++_size;
return std::make_pair(Iterator(root), true);
}
// 先提取待插入数据的 key
K key = keyOfValue(data);
// 按照二叉搜索树规则查找插入位置
Node* parent = root;
Node* cur = root;
while (cur)
{
parent = cur;
// 比较时比较的是 key,而不是整个 ValueType
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
// key 已存在,说明不能重复插入
return std::make_pair(Iterator(cur), false);
}
}
// 创建新节点
// 红黑树中新插入节点默认给红色
Node* newNode = new Node(data, RED);
newNode->_parent = parent;
// 把新节点挂接到父节点下面
if (key < keyOfValue(parent->_data))
{
parent->_left = newNode;
}
else
{
parent->_right = newNode;
}
// 如果新节点更小,就更新最小节点
if (_head->_left == _head || key < keyOfValue(_head->_left->_data))
{
_head->_left = newNode;
}
// 如果新节点更大,就更新最大节点
if (_head->_right == _head || key > keyOfValue(_head->_right->_data))
{
_head->_right = newNode;
}
++_size;
// 插入后开始做红黑树调整
// 如果父节点也是红色,就违反了不能有连续红色节点的性质
cur = newNode;
parent = cur->_parent;
while (parent != _head && parent->_color == RED)
{
// 既然父节点是红色,那么祖父节点一定存在
Node* grandfather = parent->_parent;
// 先处理父节点在祖父左边的情况
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 叔叔存在并且为红色
// 这种情况只需要变色,然后继续向上处理祖父节点
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
// 如果当前节点在父节点右边
// 说明形成了折线结构,先左旋父节点,把它转成直线结构
if (cur == parent->_right)
{
_RotateL(parent);
std::swap(cur, parent);
}
// 走到这里,一定已经转成直线结构
// 父节点染黑,祖父节点染红,然后右旋祖父节点
parent->_color = BLACK;
grandfather->_color = RED;
_RotateR(grandfather);
break;
}
}
else
{
// 父节点在祖父右边时,处理逻辑完全对称
Node* uncle = grandfather->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
_RotateR(parent);
std::swap(cur, parent);
}
parent->_color = BLACK;
grandfather->_color = RED;
_RotateL(grandfather);
break;
}
}
}
// 无论中间怎么调整,根节点最后都必须是黑色
root->_color = BLACK;
root->_parent = _head;
return std::make_pair(Iterator(newNode), true);
}
Iterator Find(const K& key)
{
KeyOfValue keyOfValue;
Node* cur = _GetRoot();
// 按照二叉搜索树规则查找
while (cur)
{
if (key < keyOfValue(cur->_data))
{
cur = cur->_left;
}
else if (key > keyOfValue(cur->_data))
{
cur = cur->_right;
}
else
{
return Iterator(cur);
}
}
// 没找到返回 end()
return End();
}
void Clear()
{
// 先递归释放整棵树
_Destroy(_GetRoot());
// 清空后把头结点恢复成初始状态
_head->_parent = nullptr;
_head->_left = _head;
_head->_right = _head;
_size = 0;
}
size_t Size() const
{
return _size;
}
bool Empty() const
{
return _size == 0;
}
private:
void _RotateL(Node* parent)
{
// 记录右孩子和右孩子的左子树
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* ppNode = parent->_parent;
// 让 parent 接住 subR 的左子树
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
// 让 subR 上升,parent 下沉到左边
subR->_left = parent;
subR->_parent = ppNode;
parent->_parent = subR;
// 把新的子树根重新接回原来的位置
if (ppNode == _head)
{
_head->_parent = subR;
}
else if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
}
void _RotateR(Node* parent)
{
// 记录左孩子和左孩子的右子树
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* ppNode = parent->_parent;
// 让 parent 接住 subL 的右子树
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
// 让 subL 上升,parent 下沉到右边
subL->_right = parent;
subL->_parent = ppNode;
parent->_parent = subL;
// 把新的子树根重新接回原来的位置
if (ppNode == _head)
{
_head->_parent = subL;
}
else if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
}
void _Destroy(Node* root)
{
// 空节点直接返回
if (root == nullptr)
{
return;
}
// 先释放左子树
_Destroy(root->_left);
// 再释放右子树
_Destroy(root->_right);
// 最后释放当前节点
delete root;
}
Node*& _GetRoot()
{
// 头结点的 parent 域就是根节点
return _head->_parent;
}
private:
Node* _head; // 头结点
size_t _size; // 有效节点个数
};
template<class K, class V>
struct MapKeyOfValue
{
const K& operator()(const std::pair<K, V>& value) const
{
// map 比较大小时,只比较 pair 中的 first
return value.first;
}
};
template<class K>
struct SetKeyOfValue
{
const K& operator()(const K& key) const
{
// set 中存的就是 key 本身,直接返回即可
return key;
}
};
namespace bite
{
template<class K, class V>
class map
{
using ValueType = std::pair<K, V>;
using RBTreeType = RBTree<K, ValueType, MapKeyOfValue<K, V>>;
public:
using iterator = typename RBTreeType::Iterator;
iterator begin()
{
// 直接复用底层红黑树的 begin
return _tree.Begin();
}
iterator end()
{
// 直接复用底层红黑树的 end
return _tree.End();
}
std::pair<iterator, bool> insert(const ValueType& data)
{
// map 插入,本质上就是向底层红黑树插入一个 pair<K, V>
return _tree.Insert(data);
}
iterator find(const K& key)
{
// 查找时只需要传 key
return _tree.Find(key);
}
void clear()
{
_tree.Clear();
}
size_t size() const
{
return _tree.Size();
}
bool empty() const
{
return _tree.Empty();
}
V& operator[](const K& key)
{
// 如果 key 不存在,会插入 pair(key, V())
// 如果 key 已存在,会直接返回原有位置
// Insert 返回值中的 first 是对应节点的迭代器
// 对迭代器解引用后得到 pair<K, V>,再取 second 就是 value
return (*(_tree.Insert(ValueType(key, V())).first)).second;
}
private:
RBTreeType _tree;
};
template<class K>
class set
{
using ValueType = K;
using RBTreeType = RBTree<K, ValueType, SetKeyOfValue<K>>;
public:
using iterator = typename RBTreeType::Iterator;
iterator begin()
{
// 直接复用底层红黑树的 begin
return _tree.Begin();
}
iterator end()
{
// 直接复用底层红黑树的 end
return _tree.End();
}
std::pair<iterator, bool> insert(const ValueType& data)
{
// set 插入,本质上就是向底层红黑树插入一个 key
return _tree.Insert(data);
}
iterator find(const K& key)
{
// 查找时只需要传 key
return _tree.Find(key);
}
void clear()
{
_tree.Clear();
}
size_t size() const
{
return _tree.Size();
}
bool empty() const
{
return _tree.Empty();
}
private:
RBTreeType _tree;
};
}
int main()
{
bite::map<std::string, int> mp;
mp.insert(std::make_pair("apple", 3));
mp.insert(std::make_pair("banana", 5));
mp["orange"] = 8;
mp["apple"]++;
std::cout << mp.size() << std::endl; // 3
std::cout << mp["apple"] << std::endl; // 4
std::cout << mp["banana"] << std::endl; // 5
std::cout << mp["orange"] << std::endl; // 8
for (auto it = mp.begin(); it != mp.end(); ++it)
{
std::cout << it->first << ":" << it->second << " ";
}
std::cout << std::endl;
auto pos1 = mp.find("banana");
if (pos1 != mp.end())
{
std::cout << pos1->first << ":" << pos1->second << std::endl; // banana:5
}
bite::set<int> st;
st.insert(10);
st.insert(3);
st.insert(7);
st.insert(3);
st.insert(15);
std::cout << st.size() << std::endl; // 4
for (auto it = st.begin(); it != st.end(); ++it)
{
std::cout << *it << " ";
}
std::cout << std::endl;
auto pos2 = st.find(7);
if (pos2 != st.end())
{
std::cout << *pos2 << std::endl; // 7
}
return 0;
}