

🌟个人主页:第七序章
🌈专栏系列:C++
目录
❄️前言:
上一篇我们学习了红黑树的实现,今天我们来学习一下基于红黑树封装的Map与Set。
☀️一、源码及框架分析
模拟实现set和map,set和map底层结构都是通过红黑树实现的,因此在set和map中直接分装一棵红黑树,然后将其接口包装下接口。
但是set是属于K搜索模型,而是map是KV搜索模型,对此虽然实现都通过以底层红黑树实现,很明显不是使用同一个红黑树,那么这样子就会很麻烦,而库中的做法是set和map调用同一棵红黑树,那么具体是如何识别set和map的呢?
首先,我们去库中找到set和map实现的代码,再将对于讲述这篇内容无关的代码删除,通过画图梳理当前的信息,得到以下这张图片
从图中可以得到一些信息,_rb<key_type, value_type>这里第二个模板参数,在set类中是以key 作为value_type,而在map是以为pair<const Key, T> 作为value_type,那么可以大致猜测红黑树就是通过value_type第二个模板参数去控制set还是map的,需要考虑到红黑树节点储存类型,还有泛型编程等因素,所以map的第二个模板参数为pair<K, V>类型。
☀️二、map第二个参数
问题:为什么map的第二个模板参数为pair<K,V>类型,而不是V类型呢?
⭐2.1 键值对本质
std::map 是一个关联容器,通常是一个 有序的二叉搜索树(红黑树实现),其目的是根据键 K 来查找与之对应的值 V。每个元素都由一个键和值组成,因此,map 的元素类型被定义为 std::pair<const K, V>,即一个键值对。
- 键 (
K):唯一标识值的标记,不能重复。 - 值 (
V):与键相关联的数据,可以修改。
为了保证键的不可变性,std::pair 中的键类型是 const K,这样可以避免误修改键,因为修改键会破坏 map 的有序性或查找机制。
⭐2.2 map 的接口设计
std::map 的设计是围绕键值对的存储和查找而设计的。它的内部元素是键值对,并且对这些键值对进行操作时需要涉及到两者的关系。因此,std::map 的接口与 pair<K, V> 紧密相关
- 插入操作:当你向
map插入元素时,插入的是一个键值对std::pair<K, V>,而不是单独的值。因为要有键才能确定值存储的位置。- 查找操作:
map.find(key)会返回一个迭代器,指向的也是一个std::pair<const K, V>,表示找到的键值对。- 迭代操作:当你遍历
map时,迭代器会指向pair<const K, V>,使得你可以访问键和对应的值。- 就是pair中K是为了跟K取得联系, std::map 中,值 V 与键 K 是独立的实体。通过将键和值一起存储在 std::pair<K, V> 中,可以有效地让键用于比较和查找操作,而值 V 则保持不变。这种设计保证了当我们执行查找或比较时,只有键会参与这些操作,而不会影响与键关联的值。(理解点在这里了)
⭐2.3 不用 V类型作为第二个模板参数
如果 map 的元素类型只是 V,那么容器将无法保存键和值的关联关系。原因是:
V类型只包含值,没有与之相关联的键。map需要将键和值配对起来才能进行查找、插入、删除等操作。map的查找、插入、删除等操作基于键K,而不仅仅是值V。pair<K, V>才能表示出键和值的完整关系。
⭐2.4 map第二个参数类型不为pair类型
int main()
{
map<string,string> m;
pair<K,V> kv = {"sort","排序"};
m.insert(kv);
}
map类中存储元素是pair<K,V>类型,但是在定义传递给map类类模板参数,由于只要需要K,V是什么,底层存储pair<K,V>类型元素是进行相对应的处理。
☀️三、红黑树内部(map与set构成模板)
将set和map与红黑树相关的图拿出来,进行梳理逻辑,可以清楚的发现他们之间的联系是十分的巧妙的,如果存在多个需要使用同一个类,使用类在类型上有所差异可以将这个类写成模板类,提供不同类型进行使用
⭐3.1 set和map的第一个模板参数会不会多余?
这里红黑树节点元素存储了set和map第二个模板参数,这里实例了两个红黑树只是这件事情是由编译器完成的。这样说明了为什么set需要存储<key,key>键值对,是为了跟map构成模板。
提出这个问题可能是考虑到第二个人模板参数存在了K,为什么还需要第一个模板参数存在,看起来就很多余,对于set而言也许是多余的,但是map而言不是的。map使用Find()和Erase()接口时,不是使用V而是使用K,至于set为什么需要多余一个K呢?因为需要跟map构成模板。
☀️四、 红黑树的改造
改造红黑树以适配map和set容器,主要涉及到如何根据这两种容器的特性来设计和实现红黑树节点的存储以及相应的操作。map和set的主要区别在于它们存储的元素类型:map存储键值对(key-value pairs),而set仅存储唯一的键值(通常是键本身作为值)。尽管如此,它们在底层数据结构(如红黑树)的实现上有很多相似之处
改造内容:
- K:key的类型
- T:如果是map,则为pair<K, V>; 如果是set,则为K
- KeyOfT:通过T来获取key的一个仿函数类
// Map 与 Set
// set -> RBTree<K, K, SetKeyOfT> _t;
// map -> RBTree<K, pair<const K, V, MapKeyOfT>> _t;
⭐4.1. 红黑树节点的改造
//枚举颜色
enum Color
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
// pair<K, V> _kv; // 上节内容使用的这种方式
T _data;
Color _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
⭐4.2 红黑树的主体框架
(1) K 是给find,erase用的,T 是给节点,insert用的;
(2) KeyOfT 是由于下面需要比较,但是比较时不知道T的类型, set是key类型的比较,map是pair类型的比较,要统一变成key的比较。
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; //const 迭代器
//其他功能的实现……
private:
Node* _root = nullptr;
};
☀️五、 红黑树的迭代器
⭐5.1 迭代器基本设计
// 通过模板来达到const的迭代器的复用
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++()
{}
Self& operator--() //右根左
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator == (const Self& s)
{
return _node == s._node;
}
bool operator != (const Self& s)
{
return _node != s._node;
}
};
⭐5.2 begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置
实现代码如下:
(注意:此步需要在RBTree类的内部实现),以便map,set的使用)
typedef RBTreeIterator<T, T&, T*> Iterator;
typedef RBTreeIterator<T, const T&, const T*> ConstIterator;
//中序遍历,找树的最左节点
Iterator Begin()
{
Node* leftMost = _root;
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return Iterator(leftMost, _root);
}
Iterator End()
{
return Iterator(nullptr, _root);
}
ConstIterator Begin() const
{
Node* leftMost = _root;
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return ConstIterator(leftMost, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
⭐5.3operator++()与operator–()
operator++()
- 右不为空,右子树的最左节点
- 右为空,沿着到根的路径找孩子是父亲左的那个祖先
operator–()
- 左不为空,左子树的最右节点
- 左为空,沿着到根的路径找孩子是父亲右的那个祖先
注意:++和–正好是完全相反的
实现代码如下:
☀️六、 红黑树相关接口的改造
⭐6.1 Find 函数的改造
查找成功,返回查找到的那个节点的迭代器,查找失败,就返回 nullptr。
Iterator* 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 Iterator(cur, _root);
}
}
return End();
}
⭐6.2 Insert 函数的改造
map 里的 operator[] 需要依赖 Insert 的返回值
pair<Iterator, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK; //根节点默认为黑色
return make_pair(Iterator(_root,_root), true);
}
KeyOfT kot;// 通过仿函数KeyOfT来比较数据的大小
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(Iterator(_root,_root), false);
}
}
cur = new Node(data);
// 注意,这里需要保存一个新增节点,以便用来返回
Node* newnode = cur;
// 新增节点。颜色红色给红色
cur->_col = RED;
// 此处省略变色的代码
return make_pair(Iterator(newnode,_root), true);
}
☀️七、Set 和 map的模拟实现
⭐7.1 Set的基本设计
set的底层为红黑树,因此只需在set内部封装一棵红黑树,即可将该容器实现出来。
为了解决 set 中 key 值不能修改的问题,在传给 RBTree 的第二个模板参数前加 const 即可。
template<class K>
class set
{// SetKeyOfT:通过T来获取key的一个仿函数类
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
// typename告诉编译器这是类型,
// 因为set不能够进行修改,所以我们用const迭代器来初始化普通迭代器,来达到不能修改的目的
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;
};
//使用 const 迭代器
void Print(const set<int>& s) {
set<int>::const_iterator it = s.end();
while (it != s.begin()) {
--it;
cout << *it << " ";
}
cout << endl;
}
//测试代码
void test_set(){
set<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;
set<int>::iterator it = s.end();
while (it != s.begin())
{
--it;
cout << *it << " ";
}
//it += 10;
cout << endl;
Print(s);
}
⭐7.2 Map的基本设计
template<class K, class V>
class map
{// MapKeyOfT:通过pair来获取kv.first的一个仿函数类
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:// map是一个key不能修改,value能够修改的结构
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();
}
// 复用RBTree中的插入
pair<iterator, bool> insert(const pair<K, V>& kv)
{
return _t.Insert(kv);
}
iterator find(const K& key)
{
return _t.Find(key);
}
// 重载map中的operatorp[]
// operatorp[] 当原数据中没有时,插入并初始化,有则修改second
V& operator[](const K& key)
{
// 没有是插入,有则是查找
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyOfT>_t;
};
//测试样例
void test_map()
{
map<string, string> dict;
dict.insert({ "sort", "排序" });
dict.insert({ "left", "左边" });
dict.insert({ "right", "右边" });
dict["left"] = "左边,剩下";
dict["insert"] = "插入";
dict["string"];
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;
}
}
☀️八、红黑树改造的完整代码及总结
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
//枚举颜色
enum Colour
{
RED,
BLACK
};
//节点类
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
// pair<K, V> _kv; // 上节内容使用的这种方式
T _data;
Colour _col;
RBTreeNode(const T& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
// 通过模板来达到const的迭代器的复用
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* leftMost = _node->_right;
while (leftMost->_left)
{
leftMost = leftMost->_left;
}
_node = leftMost;
}
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(),特殊处理,走到中序最后一个节点
Node* rightMost = _root;
while (right && 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)
{
return _node == s._node;
}
bool operator != (const Self& s)
{
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* leftMost = _root;
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return Iterator(leftMost, _root);
}
Iterator End()
{
return Iterator(nullptr, _root);
}
ConstIterator Begin() const
{
Node* leftMost = _root;
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return ConstIterator(leftMost, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
RBTree() = default;
RBTree(const RBTree& t)
{
_root = Copy(t._root);
}
RBTree& operator=(RBTree t)
{
swap(_root, t._root);
return *this;
}
~RBTree()
{
Destroy(_root);
_root = nullptr;
}
pair<Iterator, bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK; //根节点默认为黑色
return make_pair(Iterator(_root,_root), true);
}
KeyOfT kot;// 通过仿函数KeyOfT来比较数据的大小
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(Iterator(_root,_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;
// g
// p u
if (parent == grandfather->_left) {
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED) //叔叔存在并且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent; //往上面走
}
else
{
//u存在且为黑或不存在 ->变色再继续往上处理 + 变色
if (cur == parent->_left) { //cur存在那么cur一定为红色
// g
// p u
//c
//单旋,把p旋转上去,p作为子树根节点,g作为p的右
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
//双旋,将cur旋转上去,p作为cur的左,然后再旋转把cur旋转上去,g作为cur右边
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
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK; //无论什么情况根节点都为黑
return make_pair(Iterator(newnode,_root), true);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
{
return false;
}
// 随便找条路径作为参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
Iterator* 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 Iterator(cur, _root);
}
}
return End();
}
private:
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
//cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色节点的数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
void RotateL(Node* parent)
{
_rotateNum++;
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;
}
}
void RotateR(Node* parent)
{
_rotateNum++;
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parentParent == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr) return nullptr;
Node* newRoot = new Node(root->_kv);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
private:
Node* _root = nullptr;
public:
int _rotateNum = 0;
};
☀️九、本文小结
| 章节主题 | 核心内容摘要 | 关键设计 / 实现要点 |
|---|---|---|
| 一、源码及框架分析 | set 和 map 底层均基于红黑树实现,需解决两者模板复用问题;set 是 K 搜索模型,map 是 KV 搜索模型,通过红黑树第二个模板参数区分存储类型 | 1. set 的红黑树第二个模板参数为key(自身作为 value);2. map 的为pair<const K, V>(键值对) |
| 二、map 第二个参数 | map 元素本质是std::pair<const K, V>,键不可修改(保证有序性),接口设计围绕键值对展开;不能用 V 单独作为参数,否则无法关联键值 | 1. 插入、查找、迭代均操作键值对;2. 键const K防止修改破坏结构;3. 示例代码:map<string,string> m; m.insert({"sort","排序"}); |
| 三、红黑树模板构成 | 红黑树通过模板适配 set 和 map,需明确模板参数分工;set 第一个模板参数看似多余,实则为与 map 统一模板结构 | 1. 红黑树模板参数:K(查找用键类型)、T(节点存储类型)、KeyOfT(从 T 取 K 的仿函数);2. set 实例化:RBTree<K, K, SetKeyOfT>;map 实例化:RBTree<K, pair<const K, V>, MapKeyOfT> |
| 四、红黑树的改造 | 改造节点存储类型为泛型 T,设计KeyOfT仿函数统一键比较逻辑;红黑树主体框架需区分 K(查找用)和 T(存储用) | 1. 节点结构:RBTreeNode<T>,存储T _data(替代固定pair);2. 红黑树类模板:template <class K, class T, class KeyOfT> class RBTree;3. KeyOfT仿函数:从 T 中提取 K 用于比较 |
| 五、红黑树的迭代器 | 迭代器通过模板复用(支持普通 /const 类型),实现++/--、*/->等运算符;begin()指向最左节点,end()指向 nullptr | 1. 迭代器模板:template<class T, class Ref, class Ptr> struct RBTreeIterator;2. ++逻辑:右子树非空取最左节点,否则找 “孩子是父亲左” 的祖先;3. --逻辑:与++相反,end()特殊处理为最右节点 |
| 六、红黑树接口改造 | 改造Find和Insert接口适配泛型 T,依赖KeyOfT提取键;Insert返回pair<Iterator, bool>(供 map 的operator[]使用) | 1. Find:根据 K 查找,返回迭代器或end();2. Insert:根节点设为黑色,新增节点设为红色,插入后处理红黑树平衡(旋转变色) |
| 七、Set 和 map 模拟实现 | set 封装红黑树时,T 设为const K防止键修改;map 封装时 T 设为pair<const K, V>,实现operator[](依赖Insert) | 1. set:class set{ ... RBTree<K, const K, SetKeyOfT> _t; ... };2. map:class map{ ... RBTree<K, pair<const K, V>, MapKeyOfT> _t; ... };3. map 的operator[]:插入默认值并返回 value 引用 |
| 八、红黑树完整代码总结 | 包含节点、迭代器、红黑树类完整实现,以及拷贝构造、析构、平衡检查(IsBalance)等功能;核心是通过模板和仿函数实现 set/map 复用 | 1. 平衡检查:验证根节点黑色、无连续红节点、各路径黑节点数相等;2. 旋转函数:RotateL(左旋)、RotateR(右旋)处理平衡 |
🌻共勉:
以上就是本篇博客的所有内容,如果你觉得这篇博客对你有帮助的话,可以点赞收藏关注支持一波~~🥝

1053

被折叠的 条评论
为什么被折叠?



