- 1. 为什么使用红黑树
- 1.1 AVLTree
- 1.2 Red-Black Tree
- 1.2.1 红黑树特性回顾
- 2.适配key型和key-value型
- 2.1 统一传T类型
- 2.2 仿函数控制
- 2.2.2 set_KeyOfT
- 2.2.2 map_KeyOfT
- 2.2.3 RBTree template
- 2.3 revised Insert
- 2.5 revised Find
- 3. 红黑树迭代器
- 3.0 简单的基本结构
- 3.1 begin()与end()
- 3.2 operator++()
- 3.3 operator--()
- 3.4 反向迭代器
- 3.5 rbegin()和rend()
- 4. 利用红黑树迭代器
- 4.1 set
- 4.2 map
- 5. map 和 set 模拟实现
- 5.1 map
- 5.2 set
AVLTree树利用高度来控制平衡,是一种严格平衡
1.2 Red-Black Tree红黑树通过颜色互斥来控制平衡,是一种近似平衡,它保证了最长路径不超过最短路径的2倍
因为这个不同,如果插入相同的节点,红黑树绝对会比AVL树旋转少的次数
⚡️ 节点非红即黑
⚡️ 树中没有连续的红色节点
⚡️ 每一条路上的黑色节点数量是相等的
⚡️ 根节点是黑色的
⚡️ 最短路径:全黑 ; 最长路线:一红一黑
有时候我们需要的是key型,比如说是set,有时候我们需要的是key-value型的参数,那就比如说是map,那么难道我要重新写一份代码来一个支持key,一个支持key-value吗,其实不用
2.1 统一传T类型看看源码可以发现通过传入的key和value,key一定是传的key进去,value却会有不同
那么我们这里选择将之前写的红黑树进行一些改造,来实现既可以支持map也可以支持set的结构,这是T可以传普通的key也可以传pair类型,根据需要传就可以了
template2.2 仿函数控制class RBTree { typedef RBTreeNode Node; }
在RBTree的某一些接口中,比如Find和Insert就需要去比较数据的大小,才能判断位置
在源码规定中,pair的比大小有时候不仅需要first还需要second
所以如果只是按照T类型去比较大小的话,pair的比较大小方式和key的比较方式是不同的,这样直接来的话会造成Insert可以正常插入但是Find的时候找不到,因为Find的参数是K类型,而不是T类型
pairInsert(const T& data) Node* Find(const K& key)
这也是为什么RBTree这里我们要两个参数的模板,而不是只一个T就可以了,毕竟我们有时候要的是K类型,map的查找是用key来找,不是用piar来找的
templateclass RBTree { typedef RBTreeNode Node; ···
对于红黑树来说不知道自己存的数据类型存的是key还是pair,这是上层控制的,所以我们必须要一个高维度的泛型来控制
源码这里其实是通过在传一个上层函数来控制的
所以我们在这里要使用一个仿函数放在模板中,仿函数的目的就是取出key
2.2.2 set_KeyOfTtemplate2.2.2 map_KeyOfTclass set { //内部类封装一个仿函数,用来返回key struct set_KeyOfT { const K& operator()(const K& key) { return key; } }; public: bool insert(const K& k) { _t.Insert(k); return true; } private: RBTree _t; };
template2.2.3 RBTree templateclass map { //内部类封装一个仿函数,用来返回key struct map_KeyOfT { const K& operator()(const pair & kv) { return kv.first; } }; public: bool insert(const pair & kv) { _t.Insert(kv); return true; } private: RBTree ,map_KeyOfT> _t; };
红黑树的模板这里就可以更新了
template2.3 revised Insertclass RBTree { typedef RBTreeNode Node; ...
pair2.5 revised FindInsert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; return make_pair(_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 make_pair(cur, false); } } Node* newnode = new Node(data); newnode->_col = RED; if (kot(parent->_data) < kot(data)) { parent->_right = newnode; newnode->_parent = parent; } else { parent->_left = newnode; newnode->_parent = parent; } cur = newnode; // 如果父亲存在,且颜色为红色就需要处理 while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; // 关键是看叔叔 if (parent == grandfather->_left) { Node* uncle = grandfather->_right; // 情况1:uncle存在且为红 if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else // 情况2+3:uncle不存在 uncle存在且为黑 { // 情况2:单旋 if (cur == parent->_left) { _Rotate_R(grandfather); grandfather->_col = RED; parent->_col = BLACK; } else // 情况3:双旋 { _Rotate_L(parent); _Rotate_R(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else // parent == grandfather->_right { Node* uncle = grandfather->_left; // 情况1: if (uncle && uncle->_col == RED) { uncle->_col = parent->_col = BLACK; grandfather->_col = RED; cur = grandfather; parent = cur->_parent; } else // 情况2:+ 情况3: { if (cur == parent->_right) { _Rotate_L(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else // cur == parent->_left { _Rotate_R(parent); _Rotate_L(grandfather); cur->_col = BLACK; grandfather->_col = RED; } // 插入结束 break; } } } _root->_col = BLACK; return make_pair(newnode, true); }
Node* Find(const K& key)
{
KeyOfT kot;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
3. 红黑树迭代器总结一下就是:
一个模板参数控制红黑树里面存key还是存pair,实现key结构和key_value结构
一个仿函数去控制值里面用什么去比较
一个容器必须的就是迭代器,红黑树也不能少,之前AVL树中没有实现,因为红黑树很类似,于是下面来实现一下红黑树的迭代器
而map和set的迭代器本质上用的都是红黑树的迭代器,因此模拟实现map和set的难点在于红黑树
3.0 简单的基本结构From 《STL源码剖析》 红黑树迭代器的源码结构关系
如下这些重载和构造实现起来比较简单,难的主要在后面
struct __TreeIterator
{
typedef RBTreeNode Node;
typedef __TreeIterator Self;
Node* _node;
__TreeIterator(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;
}
}
3.1 begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?
答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置,也就是说是一个哨兵位的头节点,不存数据
当然也是可以不加头节点做的
typedef __TreeIterator < T, T&, T* > iterator;
iterator begin()
{
Node* left = _root;
while (left && left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
3.2 operator++()
| Situation | 寻找方式 | 节点 | 描述 |
|---|---|---|---|
| 当前节点的右树不为空 | 右树中,中序遍历的第一个节点 | 右子树最左节点 | 无需描述 |
| 右子树等于空 | 沿着三叉链往上走,找出孩子不是父亲右的那个祖先 | 孩子不是父亲右的那个祖先 | 因为cur的右为空,所以说明cur所在的子树已经访问完了,cur是parent右的话,说明parent也已经访问完了,继续往上找 |
Self& operator++()
{
if (_node->_right)
{
//右树中中序的第一个节点
Node* left = _node->_right;
while (left->_left)
{
left = left->_left;
}
_node = _left;
}
else
{//右树为空
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur==parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
3.3 operator–()
访问顺序是 右子树 根 左子树
| Situation | 寻找方式 | 节点 |
|---|---|---|
| 左不为空 | 左树中,中序遍历的最后节点 | 左子树的最右节点 |
| 左为空 | 沿着三叉链往上走,找出孩子不是父亲左的那个祖先 | 孩子不是父亲左的那个祖先 |
Self& operator--()
{
if (_node->_left)
{
Node* right = _node->_left;
while (right->_right)
{
right = right->_right;
}
_node = right;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur==parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
3.4 反向迭代器
反向迭代器可以利用一个正向迭代器来适配实现,这里就不采用迭代器萃取,因为太麻烦,所以说这里的反向迭代器采取的是一个迭代器适配器
反向迭代器的++相当于是正向迭代器的operator--,–就是正向迭代器的operator++
// 反向迭代器--迭代器适配器 template3.5 rbegin()和rend()struct Reverse_Iterator { typedef typename Iterator::reference Ref; typedef typename Iterator::pointer Ptr; typedef Reverse_Iterator Self; Iterator _it; ReverseIterator(Iterator it) :_it(it) {} Ref operator*() { return *_it; } Ptr operator->() { return _it.operator->(); } Self& operator++() { --_it; return *this; } Self& operator--() { ++_it; return* this; } bool operator!=(const Self& s) const { return _it != s._it; } bool operator==(const Self& s) const { return _it == s._it; } };
reverse_iterator rbegin()
{
Node* right = _root;
while (right && right->_right)
{
right = right->_right;
}
return reverse_iterator(iterator(right));
}
reverse_iterator rend()
{
return reverse_iterator(iterator(nullptr));
}
4. 利用红黑树迭代器
map和set的迭代器关键在于直接利用红黑树的迭代器,所以说红黑树的迭代器要写好
注意这里的typename一定要加
//这里需要加一个typename因为这里的红黑树还没有实例化,没有实例化不能去类内找内置类型,所以说要加 typedef typename RBTree4.2 map::iterator iterator; typedef typename RBTree ::reverse_iterator reverse_iterator; reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } iterator begin() { return _t.begin(); } iterator end() { return _t.end(); }
typedef typename RBTree5. map 和 set 模拟实现, map_KeyOfT>::iterator iterator; typedef typename RBTree , map_KeyOfT>::reverse_iterator reverse_iterator; reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } iterator begin() { return _t.begin(); } iterator end() { return _t.end(); }
这里有了迭代器,可以把pair中的first改成iterator去替换原来的Node*,比如在insert中的修改
也就是说其实map和set这里涉及的都是封装,本质上是把红黑树实现好,这里红黑树的erase没有实现,所以map和set这里也不封装了
5.1 maptemplate5.2 setclass map { //内部类封装一个仿函数,用来返回key struct map_KeyOfT { const K& operator()(const pair & kv) { return kv.first; } }; public: typedef typename RBTree , map_KeyOfT>::iterator iterator; typedef typename RBTree , map_KeyOfT>::reverse_iterator reverse_iterator; reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } pair insert(const pair & kv) { return _t.Insert(kv); } V& operator[](const K& key) { pair ret = insert(make_pair(key, V())); return ret.first->second; } private: RBTree , map_KeyOfT> _t; };
templateclass set { //内部类封装一个仿函数,用来返回key struct set_KeyOfT { const K& operator()(const K& key) { return key; } }; public: //这里需要加一个typename因为这里的红黑树还没有实例化,没有实例化不能去类内找内置类型,所以说要加 typedef typename RBTree ::iterator iterator; typedef typename RBTree ::reverse_iterator reverse_iterator; reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } pair insert(const K& k) { return _t.Insert(k); } private: RBTree _t; };
代码在我的github仓库https://github.com/Allen9012/cpp/tree/main/STL/%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0map%E5%92%8Cset



