map和set的底层都是红黑树,而我们在前面已经了解了红黑树的实现,所以我们现在来模拟实现map和set。
一.STL库中map和set的实现
set是key的搜索环境,而map是key/value的搜索场景,那么库里面是否实现了两棵红黑树来分别支持set和map呢?
答案是:NO!
可是set和map明明是两种不同的环境,一棵树的节点是怎么解决既存储一个ke又存储key/value的?
这就在于库中实现红黑树时多增加了一个模板参数
它们都使用了tree.h中的rb_tree,而rb_tree的节点是一个派生类,它存储的是一个value类型,而我们追根溯源到map和set中,它们分别是pair<key,value>、key。
当我们在set和map里面使用re_tree时,多传了一个参数:set多传了一个key,map多传了一个pair<key,value>。然后到底层的rb_tree时,第二个模板参数接收,然后红黑树节点用这第二个模板参数来作为节点存储的数据的类型。
那么我们是不是只需要传第二个模板参数就可以了,第一个没必要传了?况且对于set来说,两个参数是相同的。
答案当然是不行的。因为set和map还有find和erase接口,它们都是根据key来操作的,如果只传第二个模板参数,那么map的pair获取k时,就会导致函数接口不一致了,所以第一个模板参数k,是用来find和erase场景使用的。
二.模拟实现set与map
1.set与map的结构
因为我们先前已经实现了红黑树,这里我们只需要对红黑树进行一些修改即可。
我们这里也给红黑树多增加一个模板参数,用此模板参数来作为红黑树节点存储的数据的类型。所以set和map在使用红黑树作为底层传参时需要多传一个模板参数:set多传一个K,map多传一个pair<K,V>。
其实对于set来说,第二个参数确实有点多余,但是为了契合map,所以就不得不委屈一下。
Mymap.h
namespace xsc
{
template<typename K,typename V>
class map
{
private:
RBTree<K, pair<K, V>> tree;
};
}
Myset.h
namespace xsc
{
template<typename K>
class set
{
private:
RBTree<K,K> tree;
};
}
RBTree.h
//红黑树节点
template <typename Value>
struct RBTreeNode
{
Value _date;
RBTreeNode<Value>* _left;
RBTreeNode<Value>* _right;
RBTreeNode<Value>* _parent;
COLOUR colour;
RBTreeNode(const Value& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
//红黑树
template <typename K, typename Value>
class RBTree
{
typedef RBTreeNode<Value> Node;
private:
Node* _root = nullptr;
};
2.insert
我们在之前的红黑树中已经实现了insert,只不过我们改变了红黑树的一些结构,所以插入也要跟着修改。因为之前红黑树只是key/value的搜索场景,而我们在实现set和map时使用了同一棵红黑树,既要实现key也要实现key/value,因此引入了第二个模板参数,所以导致了insert的改变
因为value既可以是k,也可能是pair<K,V>。所以我们不能像以往那样直接插入一个pair,而是要插入一个value。
还有一个要注意的就是,我们插入的时候按照的是二叉搜索树的插入逻辑,大的往右走,小的往左走,这个比较的逻辑也要修改。因为直接插入的是pair,所以直接比较first;但是现在插入的value既可能是k,有可能是pair<k,v>,对于后者了来说,可以这样比较,但是对于k来说,做不到!!!
为了解决这个问题,我们可以给红黑树再引入一个模板参数, 这个模板参数是一个仿函数。
虽然我们在底层的红黑树不知道是哪种搜索场景,但是我们在上层set和map知道我们节点里面存储的是什么,所以我们可以在这两个类里面分别写一个仿函数,然后我们将这个仿函数传给红黑树,红黑树用这个仿函数来获取节点date中我们想要用来比较的数据
//列举出一部分需要比较的场景
KeyOfKey kok;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kok(cur->_data) < kok(data))
{
parent = cur;
cur = cur->_right;
}
else if (kok(cur->_data) > kok(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
3.iterator
我们之前在AVL树和红黑树中并没有涉及迭代器,而是直接使用递归的方式中序遍历了二叉树。而我们现在实现迭代器要一个一个结点的走,而不是一个函数调用直接遍历完了。
这里实现迭代器的方式与list的迭代器类似,我们用了一个类来封装结点的指针,再通过运算符重载,以此来作为迭代器。
template<typename T,typename Ref, typename Ptr>
struct RBTree_Iterator
{
typedef RBTreeNode<T> Node;
typedef RBTree_Iterator<T, Ref, Ptr> Self;
RBTree_Iterator(Node* node)
:_node(node)
{}
Node* _node;
};
3.1begin()
begin返回开始的迭代器,而红黑树只有中序遍历才有意义,所以我们要按照中序遍历二叉树,而中序的第一个,就是二叉树的最左节点(最小节点)。
Iterator Begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur);
}
这里返回的是一个迭代器对象,所以我们要用结点的指针构造出来一个迭代器对象。 因为有可能是空树,所以我们也要加一个判断条件。
3.2operator++
operator++要实现让迭代器走到下一个结点的位置,因为我们是按照中序的顺序遍历二叉树,所以++后的下一个位置就是中序的下一个位置。而中序是按照左子树-根-右子树来遍历的。
我们++时,先判断当前结点的右子树是否为空,如果不为空,就让迭代器走到右子树的最左节点
如果右子树为空,说明这棵子树已经访问完了,要走到中序的下一个位置 。我们用cur来记录当前it的位置,parent = cur->parent;如果cur是parent的左孩子,那么it就走到parent的位置,如果不是,那就继续往上走,cur = parent;parent = cur->parent;
为什么可以这样呢?右子树为空,说明说明这棵子树已经完了,而中序的顺序是左子树-根-右子树,当cur是parent的右孩子时,说明以parent为根的这颗子树也完了,要继续往上走,直到cur是parent的左孩子,这就说明以parent为根的这棵子树左子树完了,接下来就该访问根了,所以这个时候我们就让it走到parent的位置。
当我们it走到根时,且右子树不为空,那么下一个位置就是右子树的最左节点,下面的逻辑与上面的一致,右子树为空,就往上走,找到cur是parent的左孩子的根,遍历根,如果右子树不为空,就走到右子树的最左节点。
当我们走到中序的最后一个时,开始向上返回,但是一直不会遇到cur是parent的左孩子这种情况,所以cur最后会走到根,parent会走到空。此时就应该结束遍历了,所以我们可以让end()等于nullptr,当parent == nullptr时,it就走到parent的位置,然后while的判断条件就为假,退出循环。
Self& operator++()
{
//右子树不为空,走到右子树的最左节点
if (_node->_right)
{
Node* MostLeft = _node->_right;
while (MostLeft->_left)
{
MostLeft = MostLeft->_left;
}
_node = MostLeft;
}
else
{
//右子树为空,往上走,找到cur是parent的左孩子的那个parent
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
3.3end()
为了保证能够正常退出循环,而parent最后会走到空,所以我们让end也等于空,最后it == end ==nullptr,就不会进循环。
Iterator End()
{
return Iterator(nullptr);
}
3.4其他成员函数
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;
}
这里的operator->是为了针对map,因为map直接解引用的话是一个pair,还得再访问pair里面的k和v,为了简便,我们可以yongoperator->直接访问pair里面的first和second。
3.5声明红黑树迭代器
我们这里以set为例,map与之类似:
我们在set里面声明迭代器时,要声明类域,但是我们借助域作用限定符声明时,编译器并不知道我们取得这个是一个类型还是一个静态成员变化。如果是类型的话,这样写就没毛病,如果是静态成员的话,这样就不合乎语法。所以我们在前面加一个typename,意思就是告诉编译器,这是个类型,你不要报错。
typedef typename RBTree<K, K, KeyOfT>::Iterator iterator;
3.6测试迭代器
测试结果:
//测试代码
int main()
{
xsc::set<int> si;
si.insert(4);
si.insert(2);
si.insert(5);
si.insert(1);
si.insert(6);
auto it = si.begin();
while (it != si.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : si)
{
cout << e << " ";
}
cout << endl;
xsc::map<string, string> dict;
dict.insert({ "sort", "排序" });
dict.insert({ "left", "左边" });
dict.insert({ "right", "右边" });
auto di = dict.begin();
while (di != dict.end())
{
cout << di->first << ":" << di->second << endl;
++di;
}
cout << endl;
return 0;
}
3.7operator--
我们这里并没有涉及反向迭代器,但是我们通过从end开始,借助operator--来实现反向遍历:
auto it = si.end();
while (it != si.begin())
{
--it;
cout << *it << " ";
}
我们end是根节点的父亲——nullptr,所以当我们要从end开始--,我们要首先判断,如果_node == nullptr,那就找到二叉树得最右节点(最大节点),让it指向这个位置。
而在这种情况,我们要找到二叉树的最右节点就需要根节点,而我们在迭代器里面并没有根结点的指针。所以,我们在封装迭代器时还要加上根节点的指针。
template<typename T,typename Ref, typename Ptr>
struct RBTree_Iterator
{
typedef RBTreeNode<T> Node;
typedef RBTree_Iterator<T, Ref, Ptr> Self;
RBTree_Iterator(Node* node,Node* root)
:_node(node)
,_root(root)
{}
Node* _node;
Node* _root;
};
正常的中序是左子树-根-右子树,而我们反着走时顺序为右子树-根-左子树。所以operator--得实现逻辑和operator++是什么类似的。
当一个结点的左子树为空时,说明以这个节点为根的子树已经遍历完了,我们需要找到反向中序得下一个,我们让cur指向it,parent指向cur->parent;当cur是parent的右孩子时,说明此时就该访问这个parent了。
当一个结点的左子树不为空时,那就访问左子树的最右节点(最大节点)。
Self& operator--()
{
if (_node == nullptr)
{
Node* MostRight = _root;
while (MostRight && MostRight->_right)
{
MostRight = MostRight->_right;
}
_node = MostRight;
}
else if (_node->_left)
{
//左子树不为空,访问左子树的最右节点/最大节点
Node* MostRight = _node->_left;
while (MostRight->_right)
{
MostRight = MostRight->_right;
}
_node = MostRight;
}
else
{
//左子树为空,往上遍历,找到cur == parent->_right
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
需要注意的是,在左子树为空的情况时,里面的while循环必须加上parent为空的情况,因为如果it位于整棵树的根,且无左子树时,此时cur指向root,parent指向空,如果不加上这个条件,在循环内部就会出现对空指针的解引用。
4.stl库中红黑树的迭代器实现
在库中,红黑树有一个额外的哨兵位的头节点,它和根节点互为对方父亲,且它的左孩子为最左节点,右孩子为最右节点。
我们实现的迭代器end指向根节点的父亲——nullptr,而库中end指向哨兵位header。当库中采用迭代器进行中序遍历时,遍历到最右节点就开始往上返,最后parent就会走到哨兵位的头节点,也就是end,此时就遍历结束。
这种方式的便利的一点就是找最左和最右节点时很方便,直接找header的左右孩子即可。
5.const_iterator
RBTree.h
typedef RBTree_Iterator<Value, Value&, Value*> Iterator;
typedef RBTree_Iterator<Value, const Value&, Value*> Const_Iterator;
Const_Iterator Begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur, _root);
}
Const_Iterator End() const
{
return Iterator(nullptr, _root);
}
Myset.h
typedef typename RBTree<K, K, KeyOfT>::Const_Iterator const_iterator;
const_iterator begin() const
{
return tree.Begin();
}
const_iterator end() const
{
return tree.End();
}
6.set和map的迭代器不支持修改
set是key的搜索场景,map是key/value的搜索场景,而set中key是不允许被修改的,即使是普通迭代器,而对于map来说,即使是普通迭代器,它的key也不支持修改,value可以修改。
但是我们现在实现的迭代器不论是set的key还是map的key/value,都是可以修改的
那么我们要如何实现库中的效果呢?
我们迭代器使用的其实是在上层set/map传给红黑树的第二个模板参数,我们可以直接传const过去,这样无论是普通迭代器还是const迭代器都用的是const对象,自然不可以修改。只不过对于map来说,加const时,要加到pair里面的first。
加上之后,对于set来说,普通迭代器不能修改,对于map来说,普通迭代器key不能修改,value可以修改
7.map支持operator[]
在学习map的时候时我们已经知道map的operator[]其实就是调用了insert来实现的。insert的返回值是一个pair<iterator,bool>。
operator既有插入的功能,也有查找+修改的功能。
V& operator[](const K& key)
{
pair<iterator, bool> ret = tree.Insert({ key, V() });
return ret.first->second;
}
三.map/set/RBTree代码
//Mymap.h
#include "RBTree.h"
namespace xsc
{
template<typename K,typename V>
class map
{
struct KeyOfValue
{
const K& operator()(const pair<K,V>& kv)
{
return kv.first;
}
};
typedef typename RBTree<K, pair<const K, V>, KeyOfValue>::Iterator iterator;
typedef typename RBTree<K, pair<const K, V>, KeyOfValue>::Const_Iterator const_iterator;
public:
iterator begin()
{
return tree.Begin();
}
iterator end()
{
return tree.End();
}
const_iterator begin() const
{
return tree.Begin();
}
const_iterator end() const
{
return tree.End();
}
pair<iterator,bool> insert(const pair<K,V>& kv)
{
return tree.Insert(kv);
}
V& operator[](const K& key)
{
pair<iterator, bool> ret = tree.Insert({ key, V() });
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, KeyOfValue> tree;
};
}
//Myset.h
#include "RBTree.h"
namespace xsc
{
template<typename K>
class set
{
struct KeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
typedef typename RBTree<K, const K, KeyOfT>::Iterator iterator;
typedef typename RBTree<K, const K, KeyOfT>::Const_Iterator const_iterator;
public:
iterator begin()
{
return tree.Begin();
}
iterator end()
{
return tree.End();
}
const_iterator begin() const
{
return tree.Begin();
}
const_iterator end() const
{
return tree.End();
}
pair<iterator,bool> insert(const K& key)
{
return tree.Insert(key);
}
private:
RBTree<K,const K, KeyOfT> tree;
};
}
//RBTree.h
#pragma once
#include <iostream>
#include <cassert>
using namespace std;
enum COLOUR
{
RED,
BLACK
};
template <typename Value>
struct RBTreeNode
{
Value _data;
RBTreeNode<Value>* _left;
RBTreeNode<Value>* _right;
RBTreeNode<Value>* _parent;
COLOUR colour;
RBTreeNode(const Value& data)
: _data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
template<typename T,typename Ref, typename Ptr>
struct RBTree_Iterator
{
typedef RBTreeNode<T> Node;
typedef RBTree_Iterator<T, Ref, Ptr> Self;
RBTree_Iterator(Node* node, Node* root)
:_node(node)
,_root(root)
{}
Self& operator++()
{
//右子树不为空,走到右子树的最左节点
if (_node->_right)
{
Node* MostLeft = _node->_right;
while (MostLeft->_left)
{
MostLeft = MostLeft->_left;
}
_node = MostLeft;
}
else
{
//右子树为空,往上走,找到cur是parent的左孩子的那个parent
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)
{
Node* MostRight = _root;
while (MostRight && MostRight->_right)
{
MostRight = MostRight->_right;
}
_node = MostRight;
}
else if (_node->_left)
{
//左子树不为空,访问左子树的最右节点/最大节点
Node* MostRight = _node->_left;
while (MostRight->_right)
{
MostRight = MostRight->_right;
}
_node = MostRight;
}
else
{
//左子树为空,往上遍历,找到cur == parent->_right
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) const
{
return _node == s._node;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
Node* _node;
Node* _root;
};
template <typename K, typename Value, typename KeyOfKey>
class RBTree
{
typedef RBTreeNode<Value> Node;
public:
typedef RBTree_Iterator<Value, Value&, Value*> Iterator;
typedef RBTree_Iterator<Value, const Value&, Value*> Const_Iterator;
Iterator Begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur,_root);
}
Iterator End()
{
return Iterator(nullptr,_root);
}
Const_Iterator Begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur, _root);
}
Const_Iterator End() const
{
return Iterator(nullptr, _root);
}
pair<Iterator,bool> Insert(const Value& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->colour = BLACK;
return { Iterator(_root,_root),true };
}
KeyOfKey kok;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kok(cur->_data) < kok(data))
{
parent = cur;
cur = cur->_right;
}
else if (kok(cur->_data) > kok(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return { Iterator(cur,_root),false };
}
}
cur = new Node(data);
cur->colour = RED;
if (kok(parent->_data) < kok(data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 链接父亲
cur->_parent = parent;
Node* newnode = cur;
//当插入之后cur的父亲为红,则需要调整
while (parent && parent->colour == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
// g
// p u
Node* uncle = grandfather->_right;
//uncle存在且为红色
if (uncle && uncle->colour == RED)
{
parent->colour = BLACK;
uncle->colour = BLACK;
grandfather->colour = RED;
//更新cur,parent,继续向上调整
cur = grandfather;
parent = cur->_parent;
}
else
{
//uncle不存在 或者 uncle存在且为黑
if (cur == parent->_left)
{
// g
// p u
//c
RotateR(grandfather);
parent->colour = BLACK;
grandfather->colour = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->colour = BLACK;
grandfather->colour = RED;
}
break;
}
}
else
{
// g
// u p
Node* uncle = grandfather->_left;
//uncle存在且为红色
if (uncle && uncle->colour == RED)
{
parent->colour = BLACK;
uncle->colour = BLACK;
grandfather->colour = RED;
//更新cur,parent,继续向上调整
cur = grandfather;
parent = cur->_parent;
}
else
{
//uncle不存在 或者 uncle存在且为黑
if (cur == parent->_right)
{
// g
// u p
// c
RotateL(grandfather);
parent->colour = BLACK;
grandfather->colour = RED;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->colour = BLACK;
grandfather->colour = RED;
}
break;
}
}
}
//保证根永远是黑色
_root->colour = BLACK;
return { Iterator(newnode,_root),true };
}
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;
}
int Height(){return _Height(_root);}
int Size(){return _Size(_root);}
private:
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;
}
int _Size(Node* root){
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
void RotateR(Node* parent){
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pParent = parent->_parent;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
{
pParent->_left = subL;
}
else
{
pParent->_right = subL;
}
subL->_parent = pParent;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pParent = parent->_parent;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
{
pParent->_left = subR;
}
else
{
pParent->_right = subR;
}
subR->_parent = pParent;
}
}
private:
Node* _root = nullptr;
};
完~