目录
一、改造红黑树
1、模板T改造节点
2、提取节点中的key
3、迭代器类
operator++
operator--
4、改造insert
5、红黑树迭代器
6、 普通迭代器构造const迭代器
二、set
三、map
在stl中map和set的结构中,他们都使用一个红黑树进行封装。
由上图可知,set传给红黑树节点的两个模板参数都是key,而map传给的红黑树的第一个模板参数是key、第二个参数是pair,因此我们首先对上一篇文章中的红黑树进行改造。
#pragma once
enum Colour
{
RED,
BLACK,
};
template
struct RBTreeNode
{
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
T _data;
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
template
struct __RBTreeIterator
{
typedef RBTreeNode Node;
typedef __RBTreeIterator Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator& it)
:_node(it._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,找左子树最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
// 2、左为空,孩子是父亲的右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
template
class RBTree
{
typedef RBTreeNode Node;
public:
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
public:
typedef __RBTreeIterator itertaor;
typedef __RBTreeIterator const_itertaor;
itertaor begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return itertaor(cur);
}
itertaor end()
{
return itertaor(nullptr);
}
const_itertaor begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_itertaor(cur);
}
const_itertaor end() const
{
return const_itertaor(nullptr);
}
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
pair Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(itertaor(_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(itertaor(cur), false);
}
}
cur = new Node(data);
Node* newnode = cur;
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(itertaor(newnode), true);;
}
bool IsBalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
int Height()
{
return _Height(_root);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (benchmark != blackNum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
{
++blackNum;
}
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)
{
cout << "存在连续的红色节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppnode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else
{
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};
T
改造节点我们需要将节点结构体RBTreeNode
中的数据成员_data
的类型改为模板参数T
。这样,每个节点就可以存储不同类型的数据,以适应map的节点存储pair结构,set的节点存储key。
template
struct RBTreeNode
{
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
T _data;
Colour _col;
RBTreeNode(const T& data)
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
在上述代码中,我们可以看到`RBTree`类的定义如下:
template
class RBTree
{
// ...
};
其中,`K`是键的类型,`T`是值的类型,`KeyOfT`是一个辅助类也叫作仿函数,用于从值中提取键。在`set`中,键和值的类型相同,因此`KeyOfT`可以直接使用`K`。而在`map`中,键和值的类型不同,因此我们需要对`KeyOfT`进行修改。
对于`map`类,我们需要将`KeyOfT`修改为适应`pairoperator()
,这个辅助类的作用是从`pair
template
class map
{
struct MapKeyOfT
{
const K& operator()(const pair& kv)
{
return kv.first;
}
};
private:
RBTree, MapKeyOfT> _t;
};
对于`set`类,我们需要将`KeyOfT`修改为配合map使用的辅助类。我们可以定义一个仿函数`SetKeyOfT`, 通过重载operator()直接返回key。同时,我们将第三个模板参数修改为’SetKeyOfT'。
template
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
private:
RBTree _t;
};
template
struct __RBTreeIterator
{
typedef RBTreeNode Node;
typedef __RBTreeIterator Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator& it)
:_node(it._node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++(){}
Self& operator--(){}
};
这段代码定义了一个模板类__RBTreeIterator
,它有三个模板参数:T
、Ref
和Ptr
。
T
:表示节点中存储的数据类型。Ref
:表示解引用操作符*
返回的引用类型。Ptr
:表示箭头操作符->
返回的指针类型。这个迭代器类用于遍历红黑树,并提供了对节点数据的访问功能。
在这个迭代器类中,有一个成员变量Node* _node
,它指向红黑树中的一个节点。
__RBTreeIterator(Node* node)
用于初始化迭代器对象,将_node
指针指向传入的节点。__RBTreeIterator(const __RBTreeIterator& it)
用于从普通迭代器构造const迭代器。它将传入迭代器的_node
指针赋值给当前迭代器的_node
成员变量。*
,使得可以通过迭代器访问节点的数据。它返回的是Ref
类型的引用,可以直接修改节点的数据。->
,使得可以通过迭代器访问节点的数据。它返回的是Ptr
类型的指针,可以通过指针访问节点的数据成员。!=
,用于比较两个迭代器是否不相等。它比较两个迭代器的_node
指针是否相等。++
,使得可以将迭代器指向下一个节点。如果当前节点有右子节点,则下一个节点是右子树的最左节点;否则,沿着到根的路径,找到第一个孩子是父节点左孩子的祖先节点。--
,使得可以将迭代器指向上一个节点。如果当前节点有左子节点,则上一个节点是左子树的最右节点;否则,沿着到根的路径,找到第一个孩子是父节点右孩子的祖先节点。 Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
首先,函数检查当前节点的右子节点是否存在。如果存在,说明存在右子树,下一个节点就是右子树中的最左节点。因此,函数将迭代器指向右子树中的最左节点,并结束函数的执行。
如果当前节点的右子节点为空,说明不存在右子树。在这种情况下,函数需要沿着到根的路径向上查找,直到找到一个节点,该节点是其父节点的左子节点。这是因为在红黑树中,右子树为空的节点的下一个节点是其父节点。
函数使用两个指针变量 cur 和 parent 来进行查找。
最后,函数返回迭代器自身的引用,以支持链式操作。
Self& operator--()
{
if (_node->_left)
{
// 1、左不为空,找左子树最右节点
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
// 2、左为空,孩子是父亲的右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
首先,函数检查当前节点的左子节点是否存在。如果存在,说明存在左子树,前一个节点就是左子树中的最右节点。因此,函数将迭代器指向左子树中的最右节点,并结束函数的执行。
如果当前节点的左子节点为空,说明不存在左子树。在这种情况下,函数需要沿着到根的路径向上查找,直到找到一个节点,该节点是其父节点的右子节点。这是因为在红黑树中,左子树为空的节点的前一个节点是其父节点。
函数使用两个指针变量 cur
和 parent
来进行查找。
cur
初始化为当前节点,将 parent
初始化为 cur
的父节点。parent
存在且 cur
是 parent
的左子节点,就继续向上查找。cur
更新为 parent
,将 parent
更新为 cur
的父节点。最后,函数返回迭代器自身的引用,以支持链式操作。
1. 修改函数的参数类型为 `const T& data`,以接受要插入的数据。
返回类型 pair
是为了提供更多的信息给调用者,以便在插入操作后进行进一步的处理。
pair Insert(const T& data)
iterator
:表示插入节点的迭代器。通过返回迭代器,调用者可以方便地访问插入的节点,进行后续的操作,如修改节点的值、删除节点等。
bool
:表示插入是否成功的标志。通过返回布尔值,调用者可以知道插入操作是否成功。如果插入成功,布尔值为 true
;如果插入失败(节点已存在),布尔值为 false
。
2. 将 `pair
cur = new Node(data);
3. 在函数内部,将 `_kv` 替换为 `_data`,以匹配节点的数据成员名称。
if (cur->_data < data){
// ...
}
else if (cur->_data > data){
// ...
}
else{//查找失败
return make_pair(iterator(cur), false);
}
4. 新增的 KeyOfT kot;
是为了创建 KeyOfT
类型的对象 kot
,用于从节点数据类型 T
中提取键值。
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < kot(data))
{//..}
else if (kot(cur->_data) > kot(data))
{//..}
else
{//..}
}
5. 在修改后的 Insert
函数中,新增的 Node* newnode = cur;
是为了在返回 pair
时,将插入的节点的迭代器保存下来。
Node* newnode = cur;
iterator(cur)
创建的,其中 cur
是指向新插入节点的指针。然而,在后续的操作中,cur
的值可能会发生变化,因为在红黑树的调整过程中,节点的位置可能会发生改变。cur
的值保存在 newnode
中。这样,在返回 pair
时,我们使用 iterator(newnode)
创建迭代器,确保迭代器指向插入的节点。Node* newnode = cur;
的目的是为了保存插入节点的指针,以便在返回迭代器时使用。这样可以确保返回的迭代器指向正确的节点,而不受后续操作的影响。6. 在函数的返回语句中,使用 `make_pair` 创建一个 `pair` 对象,其中包含插入的迭代器和插入是否成功的标志。
return make_pair(iterator(newnode), true);
template
class RBTree
{
typedef RBTreeNode Node;
public:
typedef __RBTreeIterator itertaor;
typedef __RBTreeIterator const_itertaor;
itertaor begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return itertaor(cur);
}
itertaor end()
{
return itertaor(nullptr);
}
const_itertaor begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_itertaor(cur);
}
const_itertaor end() const
{
return const_itertaor(nullptr);
}
iterator
和 const_iterator
,分别对应可修改和只读的迭代器。这些迭代器是通过 __RBTreeIterator
结构体模板实例化得到的。begin()
和 end()
函数,分别用于返回红黑树的起始迭代器和结束迭代器。begin()
函数中,我们从根节点开始,沿着左子节点的路径向下遍历,直到找到最左边的节点。这个节点就是红黑树中最小的节点,也是起始节点。我们将其作为参数传递给 __RBTreeIterator
构造函数,创建一个迭代器对象,并将其返回。end()
函数中,我们直接返回一个迭代器对象,其指针成员 _node
设置为 nullptr
,表示结束位置。const
重载的版本,逻辑与非 const
版本相同,只是返回的是 const_iterator
类型的迭代器对象。template
struct __RBTreeIterator
{
typedef RBTreeNode Node;
typedef __RBTreeIterator Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
// 1、typedef __RBTreeIterator itertaor; 拷贝构造
// 2、 typedef __RBTreeIterator const_itertaor;
// 支持普通迭代器构造const迭代器的构造函数
__RBTreeIterator(const __RBTreeIterator& it)
:_node(it._node)
{}
}
const __RBTreeIterator& it
部分没有显式的强制类型转换。它是一个常量引用,用于接收普通迭代器对象的引用作为参数,如果需要将普通迭代器对象转换为const迭代器对象,编译器会自动进行隐式类型转换。const
修饰符,这意味着它是一个常量引用。当使用普通迭代器对象调用构造函数时,编译器会进行隐式类型转换,将普通迭代器对象转换为const迭代器对象。#pragma once
#include "RBTree.h"
namespace MySet
{
template
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree::const_itertaor iterator;
typedef typename RBTree::const_itertaor const_iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
pair insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree _t;
};
}
#pragma once
#include "RBTree.h"
namespace MyMap
{
template
class map
{
struct MapKeyOfT
{
const K& operator()(const pair& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree, MapKeyOfT>::itertaor iterator;
iterator begin()
{
return _t.begin();
}
iterator end()
{
return _t.end();
}
V& operator[](const K& key)
{
pair ret = _t.Insert(make_pair(key, V()));
return ret.first->second;
}
pair insert(const pair& kv)
{
return _t.Insert(kv);
}
private:
RBTree, MapKeyOfT> _t;
};
MyMap
中,定义了一个内部结构体MapKeyOfT
,它是一个仿函数,用于从键值对中提取键。在这个仿函数中,重载了函数调用操作符operator()
,接受一个键值对kv
作为参数,并返回该键值对中的键first
。map
类的定义。它使用了两个模板参数K
和V
,分别表示键和值的类型。在map
类中,使用了RBTree
类来实现红黑树的功能。RBTree
的模板参数为K
、pair
和MapKeyOfT
,表示键的类型、键值对的类型和键提取仿函数的类型。map
类中定义了一个迭代器类型iterator
,它是RBTree, MapKeyOfT>::iterator
的别名。这个迭代器类型用于遍历映射中的键值对。map
类提供了begin
和end
方法,用于返回映射的起始迭代器和结束迭代器。begin
方法返回的是红黑树的起始迭代器,而end
方法返回的是红黑树的结束迭代器。map
类还重载了下标操作符[]
,使得可以通过键访问对应的值。它接受一个键key
作为参数,并返回对应的值的引用。如果键不存在于映射中,则会自动插入一个新的键值对,并返回对应值的引用。map
类还提供了insert
方法,用于向映射中插入键值对。它接受一个键值对kv
作为参数,并返回一个pair
对象,其中包含一个迭代器和一个布尔值。迭代器指向插入的键值对,布尔值表示插入是否成功。map
类中有一个私有成员变量_t
,它是一个RBTree, MapKeyOfT>
类型的对象,用于存储映射的键值对。