目录
1、实现红黑树的泛型
2、set和map的插入
3、set和map的迭代器
3.1 operator++
3.2 operator--
3.3 const迭代器
4、find
5、map的operator[]
6、完整代码
6.1 红黑树
6.2 set
6.3 map
1、实现红黑树的泛型
我们先引入上一节中写的红黑树
enum Colour
{
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
pair<K, V> _kv;
// 三叉链
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
Colour _col; // 结点颜色
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree() = default;
~RBTree()
{
Destory(_root);
_root = nullptr;
}
RBTree(const RBTree<K, V>& kv)
{
_root = Copy(kv._root);
}
RBTree operator=(RBTree<K, V> kv)
{
swap(_root, kv._root);
}
bool Insert(const pair<K, V>& kv)
{
// 若_root为空,直接让新插入的结点变成根
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 找到要插入的位置后,根据传过来的值创建一个结点,然后判断其是父亲节点的左边还是右边,链接上去
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 因为现在是三叉链结果,还要链接一下父亲结点
cur->_parent = parent;
// 链接上去后要判断一下父亲颜色是否为红色,若是红色则需要处理
while (parent && parent->_col == RED)
{
// 若父亲是红色,则看叔叔是什么颜色
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
else
{
Node* uncle = grandfather->_left;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
}
_root->_col = BLACK;
return true;
}
void InOrder()
{
_InOrder(_root);
}
int Height()
{
return _Height(_root);
}
int Size()
{
_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);
}
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;
}
private:
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
if (blackNum != refNum)
{
cout << "存在黑色结点的数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "存在连续的红色结点" << 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 RotateL(Node* parent) // 左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentParent = parent->_parent;
// 1. 将subRL变成parent的右子树
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
// 2. 将parent变成subR的左子树
subR->_left = parent;
parent->_parent = subR;
// 3. 建立subR与parent的父亲结点parentParent的关系
if (parentParent == nullptr) // 说明parent就是根结点
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
parentParent->_left = subR;
else
parentParent->_right = subR;
subR->_parent = parentParent;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentParent = parent->_parent;
// 1. 将subLR变成parent的左子树
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
// 2. 将parent变成subL的右子树
subL->_right = parent;
parent->_parent = subL;
// 3. 建立subL与parent的父亲结点parentParent的关系
if (parentParent == nullptr) // 说明parent就是根结点
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
parentParent->_left = subL;
else
parentParent->_right = subL;
subL->_parent = parentParent;
}
}
void Destory(Node* root)
{
if (root == nullptr) return;
Destory(root->_left);
Destory(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr) return nullptr;
Node* newNode = new Node(root->_kv);
newNode->_left = Copy(root->_left);
newNode->_right = Copy(root->_right);
return newNode;
}
void _InOrder(Node* _root)
{
if (_root == nullptr) return;
_InOrder(_root->_left);
cout << _root->_kv.first << "--" << _root->_kv.second << endl;
_InOrder(_root->_right);
}
Node* _root;
};
在这颗红黑树中,结点中默认存储的类型是pair,这是map中的数据类型,并不符合set。通过观察STL源码可知,set和map使用的是同一个红黑树类模板,所以我们需要对红黑树进行改造,使红黑树可以通过模板参数来控制结点中存放的数据类型
所以将红黑树类的第二个模板参数,作为结点数据的类型
template<class K, class T>
class RBTree
{
typedef RBTreeNode<T> Node;
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)
, _col(RED)
{}
};
set类的模板参数是Key和Key
namespace cxf
{
template<class K>
class set
{
private:
RBTree<K, K> _t;
};
}
map类的模板参数是Key和Key、Value组成的键值对
namespace cxf
{
template<class K,class V>
class map
{
private:
RBTree<K, pair<K,V>> _t;
};
}
此时就会有疑问了,红黑树的结点只需要使用红黑树类的第二个模板参数就能定义,那么红黑树类的第一个模板参数还需要吗?
在set中确实是可有可无的,因为两个模板参数相同,但在map中是需要的,因为map中的find和erase的参数都是Key
为了让红黑树能够是K模型或KV模型,现在并没有完全修改完,后面还需要对红黑树进行修改,在后面一步一步讲解
2、set和map的插入
根据上面的修改,我们可以将红黑树中的Insert修改成
bool Insert(const T& data)
{
// 若_root为空,直接让新插入的结点变成根
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data.first < data.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_data.first > data.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 找到要插入的位置后,根据传过来的值创建一个结点,然后判断其是父亲节点的左边还是右边,链接上去
cur = new Node(data);
if (parent->_data.first < data.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 因为现在是三叉链结果,还要链接一下父亲结点
cur->_parent = parent;
// 链接上去后要判断一下父亲颜色是否为红色,若是红色则需要处理
while (parent && parent->_col == RED)
{
// 若父亲是红色,则看叔叔是什么颜色
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
else
{
Node* uncle = grandfather->_left;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
}
_root->_col = BLACK;
return true;
}
此时是有问题的,因为T可能是Key或者pair<Key,Value>,上面是将其统一处理为pair,若T是Key,则没有first,但若是统一处理成Key,则没办法比较pair,因为pair的比较规则是先比较first,first相同再比较second。这里需要的比较规则是若T是Key,则直接比较,若T是pair,则比较对象的first,所以这里需要实现一个仿函数。这个仿函数的作用是传结点的数值域过去,若数值域是Key,则返回Key,若数值域是pair,则返回first
namespace cxf
{
template<class K>
class set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
bool insert(const K& key)
{
_t.Insert(key);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
namespace cxf
{
template<class K,class V>
class map
{
struct MapKeyOfT
{
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
public:
bool insert(const pair<K,V>& kv)
{
_t.Insert(kv);
}
private:
RBTree<K, pair<K,V>, MapKeyOfT> _t;
};
}
所以,红黑树需要增加一个模板参数
template<class K, class T, class KeyOfT>
bool Insert(const T& data)
{
// 若_root为空,直接让新插入的结点变成根
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点
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);
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 (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
else
{
Node* uncle = grandfather->_left;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
}
_root->_col = BLACK;
return true;
}
3、set和map的迭代器
3.1 operator++
set和map都是复用红黑树的迭代器,所以我们需要先实现红黑树的迭代器
红黑树的迭代器与list的迭代器是类似的,成员变量就是一个结点的指针,然后通过赋值运算符重载来修饰迭代器。最大的区别在于operator++,list的operator++是直接到下一个结点,红黑树的operator++是需要使用中序遍历的遍历方式,++后就到中序的下一个。begin返回的就是最左边的结点的迭代器
情况一:迭代器所在结点的右子树不为空
按照左中右的遍历方式,此时就是刚刚遍历完中,接下来需要去遍历当前结点的右子树按中序遍历的第一个结点,也就是最左边的结点
例如上面的红黑树,it指向13,右子树不为空,++后就应该到右子树的最左边结点,也就是15
情况二:迭代器所在结点的右子树为空
1. 若当前结点是父亲的左孩子,此时就是刚刚遍历完左,接下来需要去遍历中,则++后的结果是到父亲结点
例如上面的红黑树,it指向15,右子树为空,并且15是父亲17的左孩子,所以++后就到了17
2. 若当前结点是父亲的右孩子,此时就是刚刚遍历完右,说明当前子树已经遍历完了,有可能整棵树都遍历完了,也有可能只是遍历完了一颗子树。此时就需要一直向上找,直到找到的结点是父亲的左孩子,然后++后的结果就是这个父亲,或者父亲为空,表示整棵树遍历完了
例如上面的红黑树,it指向6,右子树为空,并且6是父亲1的右孩子,向上找,会发现1是其父亲8的左孩子,所以++后的结果就是8。再例如it指向27,右子树为空,并且27是父亲25的右孩子,所以一直向上找,会发现直到根节点13,都没有发现27到根结点的路径上有是左孩子的结点,到根节点都其父亲就是空,表示整棵树都遍历完了
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;
// 只要父亲不为空或者cur是父亲的右孩子就一直向上找
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
红黑树的begin是返回中序遍历的第一个结点,也就是整棵树最左边的结点,end是返回一个空指针的迭代器,因为当遍历完后,返回的是一个空,这样能让遍历完的结果和end一致
typedef RBTreeIterator<T> Iterator;
Iterator Begin()
{
Node* leftMost = _root;
// 前面有leftMost是为了防止_root为空
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return Iterator(leftMost);
}
Iterator End()
{
return Iterator(nullptr);
}
在set和map中,直接复用红黑树的迭代器即可
set中
typedef typename RBTree<K, K, SetKeyOfT>::Iterator iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
map中
typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::Iterator iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
在这里需要说明一下,STL中用来实现set和map的红黑树与我们上面实现的是有一点区别的
STL中的红黑树是有一个哨兵位结点的,哨兵位结点为红色,哨兵位结点的left指向最左边的结点,right指向最右边的结点,哨兵位结点的父亲指向头结点,头结点的父亲指向哨兵位结点。begin返回的是哨兵位结点的left,end返回的是哨兵位结点,因为哨兵位是根节点的父亲。
3.2 operator--
map和set的迭代器是双向迭代器,所以我们还需要实现operator--
operator--是中序的逆过程,也就是右根左
情况一:迭代器所在的结点左子树不为空
按照右根左的遍历方式,此时就是刚刚遍历完根,接下来需要去遍历当前结点的左子树按中序遍历逆过程的第一个结点,也就是最右边的结点
例如上面的红黑树,it指向13,左子树不为空,--后就应该到左子树的最右边结点,也就是11
情况二:迭代器所在的结点的左子树为空
1. 若当前结点是父亲的右孩子,此时就是刚刚遍历完右,接下来需要去遍历根,则--后的结果是到父亲结点
例如上面的红黑树,it指向27,左子树为空,并且27是父亲25的右孩子,所以--就到25
2. 若当前结点是父亲的左孩子,此时就是刚刚遍历完左,说明当前子树已经遍历完了,有可能整棵树都遍历完了,也有可能只是遍历完了一颗子树。此时就需要一直向上找,直到找到的结点是父亲的右孩子,然后--后的结果就是这个父亲,或者父亲为空,表示整棵树遍历完了
例如上面的红黑树,it指向22,左子树为空,并且22是父亲25的左孩子,向上找,会发现25是其父亲17的右孩子,所以--后的结果就是17。再例如it指向1,左子树为空,并且1是父亲8的左孩子,所以一直向上找,会发现直到根节点13,都没有发现1到根结点的路径上有是右孩子的结点,到根节点都其父亲就是空,表示整棵树都遍历完了
Self& operator--()
{
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;
}
我们现在已经实现了operator--,那如何使用--从尾遍历一整课树呢?若是使用std中的set可以像下面这样
void test_set()
{
std::set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
s.insert(6);
std::set<int>::iterator it = s.end();
while (it != s.begin())
{
--it;
cout << *it << " ";
}
}
但是我们自己实现的是不行的,因为我们的end返回的是一个空指针的迭代器,当这个迭代器调用operator--时,是没办法解引用的。那我们要如何实现这个功能呢?我们先来了解一下STL是如何实现的。STL中的红黑树调用end返回的是哨兵位结点的迭代器,按道理说--后是不会到最右边结点的,之所以能到最右边结点,是因为在operator--中进行了特殊处理(当迭代器指向哨兵位时,--就让其指向中序的最后一个结点),所以,我们也可以模仿,当迭代器中的指针为空时,--就让其指向中序的最后一个结点
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;
}
此时需要用到根节点,所以让迭代器类增加一个成员变量,初始化(构造函数)时将红黑树的根结点传过来即可。注意:若旋转后根发生了变化,会造成迭代器失效。不要GetRoot,因为没有树对象
3.3 const迭代器
首先,我们先来实现红黑树的const_iterator
template<class T, class Ref, class Ptr>
class RBTreeIterator
{
public:
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;
// 只要父亲不为空或者cur是父亲的右孩子就一直向上找
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;
}
};
typedef RBTreeIterator<T, T&,T*> Iterator;
typedef RBTreeIterator<T, const T&,const T*> ConstIterator;
Iterator Begin()
{
Node* leftMost = _root;
// 前面有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;
// 前面有leftMost是为了防止_root为空
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return ConstIterator(leftMost, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
然后来实现map、set中的const迭代器。map、set的迭代器与之前的一些不同。先说set,应当不能通过迭代器去修改结点的值,因为若是可以通过迭代器随便修改结点的值,那这棵树就不是二叉搜索树了。在STL源码中,set的iterator和const_iterator用的都是红黑树中的iterator
map是不能通过迭代器去修改结点的first的值,second的值是可以修改的,原因与上面相同。在STL源码中,map的iterator用的树红黑树的iterator,const_itertaor用的是红黑树的const_iterator,但是map类的成员变量传参时,pair的第一个参数是const的。
我们这里都采用STL源码中map的处理方式。
RBTree<K, const K, SetKeyOfT> _t;
RBTree<K, pair<const K,V>, MapKeyOfT> _t;
4、find
修改红黑树的Find,使之返回迭代器
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();
}
5、map的operator[]
map的operator[]需要调用insert,所以我们需要先对红黑树的insert进行修改,使之返回值变成pair
pair<Iterator, bool> Insert(const T& data)
{
// 若_root为空,直接让新插入的结点变成根
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(Iterator(_root, _root).true);
}
// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点
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(Iterator(cur, _root), false);
}
}
// 找到要插入的位置后,根据传过来的值创建一个结点,然后判断其是父亲节点的左边还是右边,链接上去
cur = new Node(data);
Node* newNode = cur;
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 (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
else
{
Node* uncle = grandfather->_left;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
}
_root->_col = BLACK;
return make_pair(Iterator(newNode, _root), true);
}
注意:这里面若是插入成功的情况,cur可能会向上检查而不指向新插入的结点,所以要用一个newNode在刚创建cur时就记录一下
6、完整代码
6.1 红黑树
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)
, _col(RED)
{}
};
template<class T, class Ref, class Ptr>
class RBTreeIterator
{
public:
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;
// 只要父亲不为空或者cur是父亲的右孩子就一直向上找
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;
// 前面有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;
// 前面有leftMost是为了防止_root为空
while (leftMost && leftMost->_left)
{
leftMost = leftMost->_left;
}
return ConstIterator(leftMost, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
RBTree() = default;
~RBTree()
{
Destory(_root);
_root = nullptr;
}
RBTree(const RBTree<K,T, KeyOfT>& kv)
{
_root = Copy(kv._root);
}
RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> kv)
{
swap(_root, kv._root);
}
pair<Iterator, bool> Insert(const T& data)
{
// 若_root为空,直接让新插入的结点变成根
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(Iterator(_root, _root),true);
}
// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点
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(Iterator(cur, _root), false);
}
}
// 找到要插入的位置后,根据传过来的值创建一个结点,然后判断其是父亲节点的左边还是右边,链接上去
cur = new Node(data);
Node* newNode = cur;
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 (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; // 旋转 + 变色后就可以直接退出循环了
}
}
else
{
Node* uncle = grandfather->_left;
// 如果叔叔结点存在且为红色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = grandfather->_parent;
}
// 叔叔结点不存在或者是黑色
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
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);
}
int Height()
{
return _Height(_root);
}
int Size()
{
_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)
{
if (blackNum != refNum)
{
cout << "存在黑色结点的数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "存在连续的红色结点" << 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 RotateL(Node* parent) // 左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentParent = parent->_parent;
// 1. 将subRL变成parent的右子树
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
// 2. 将parent变成subR的左子树
subR->_left = parent;
parent->_parent = subR;
// 3. 建立subR与parent的父亲结点parentParent的关系
if (parentParent == nullptr) // 说明parent就是根结点
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
parentParent->_left = subR;
else
parentParent->_right = subR;
subR->_parent = parentParent;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentParent = parent->_parent;
// 1. 将subLR变成parent的左子树
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
// 2. 将parent变成subL的右子树
subL->_right = parent;
parent->_parent = subL;
// 3. 建立subL与parent的父亲结点parentParent的关系
if (parentParent == nullptr) // 说明parent就是根结点
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
parentParent->_left = subL;
else
parentParent->_right = subL;
subL->_parent = parentParent;
}
}
void Destory(Node* root)
{
if (root == nullptr) return;
Destory(root->_left);
Destory(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr) return nullptr;
Node* newNode = new Node(root->_kv);
newNode->_left = Copy(root->_left);
newNode->_right = Copy(root->_right);
return newNode;
}
void _InOrder(Node* _root)
{
if (_root == nullptr) return;
_InOrder(_root->_left);
cout << _root->_kv.first << "--" << _root->_kv.second << endl;
_InOrder(_root->_right);
}
Node* _root;
};
6.2 set
namespace cxf
{
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;
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
iterator find(const K& key)
{
return _t.Find(key);
}
private:
RBTree<K, const K, SetKeyOfT> _t;
};
}
6.3 map
namespace cxf
{
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;
pair<iterator, bool> insert(const pair<K,V>& kv)
{
return _t.Insert(kv);
}
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
iterator find(const K& key)
{
return _t.Find(key);
}
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;
};
}
因为红黑树里面有构造、析构等默认成员函数,所以set、map中不需要实现这些默认成员函数