✨前言✨
📘 博客主页:to Keep博客主页
🙆欢迎关注,👍点赞,📝留言评论
⏳首发时间:2024年5月10日
📨 博主码云地址:博主码云地址
📕参考书籍:《C++ Primer》《C++编程规范》
📢编程练习:牛客网+力扣网
**由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!
全文的完整代码
1 认识map与set
序列式容器:像vector,list底层是线性的数据结构,它们属于STL中的序列式容器。
关联式容器:像map与set底层是红黑树,属于非线性的结构,属于关联式容器
map与set都是通过关键字Key比较,通过红黑树的规则进行存储的,是有序的!但是不同的是,set是去重的集合,里面只有Key,而map也是去重的集合,但是里面存储的K-V结构!那么在map中是如何存储K-V值的呢?实际上,map是用pair<k,v>类型来存储K-V结构的:
key就是pair<k,v>类型中的first,value就是second!而我们可以着重来理解一下map重载[]这方法
通过以上两张图片,我们就可以知道,重载[]是需要你在[]内填入相应的key值就会返回对应的value值,如何取到对应的value呢?我们可以看到,该方法是复用了insert方法,insert方法是怎么样的呢?
我们可以看到insert是插入pair<k,v>类型的,返回的是一个pair<iterator,bool>类型的,结合上面重载[]的返回值,我们就可以知道,我们取出pair<iterator,bool>对象中的first也就是iterator,对迭代器进行解引用就是pair<k,v>对象,然后取second就是value了!
2 改造红黑树
下面让我们看看set与map底层的源码是怎么样的,set的源码如图所示:
map的源码如图所示:
底层的红黑树是以k,v结构,为了复用这段代码,从上图中我们可以发现,set是传入了两个key值,而map是传入一个key值一个pair类型的值,既然这样,我们是否可以只传入第二个value值呢?我们红黑树就写一个v结构的?显然是不可以的,比如我们在查找时或者需要进行比较,是根据key值来进行查找与比较的,这样set与map就保持了一致!有了上述想法,在之前的红黑树的基础上,改造如下:
#include <assert.h>
enum Colour
{
RED,
BLACK
};
template <class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
T _data;
RBTreeNode(const T& data)
:_left(nullptr),
_right(nullptr),
_parent(nullptr),
_col(RED),
_data(data)
{}
};
//Comapre是一个仿函数,用来取出key值
template <class K, class T,class Compare>
class RBTree
{
public:
typedef RBTreeNode<T> Node;
public:
bool Insert(const T& data)
{
//1 寻找要插入的位置
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
Node* parent = _root;
Node* cur = _root;
Compare com;
while (cur)
{
if (com(cur->_data) > com(data))
{
parent = cur;
cur = cur->_left;
}
else if (com(cur->_data) < com(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
if (cur)
cur->_parent = parent;
}
cur = new Node(data);
if (com(data) > com(parent->_data))
{
parent->_right = cur;
}
else {
parent->_left = cur;
}
cur->_parent = parent;
Node* newnode = cur;
//开始改变颜色
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
//parent节点在祖父节点的左边
if (parent == grandfather->_left)
{
//第一种情况,需要变色处理
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
uncle->_col = BLACK;
parent->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
//第二种情况 uncle节点为空或者是黑色节点
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
//由于根节点变成了黑色,那就不用再往上判断了
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
uncle->_col = BLACK;
parent->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
//第二种情况 uncle节点为空或者是黑色节点
if (cur == parent->_left)
{
RotateR(parent);
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
//由于根节点变成了黑色,那就不用再往上判断了
break;
}
}
}
//将根节点的颜色变成黑色就行了
_root->_col = BLACK;
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
subR->_left = parent;
parent->_right = subRL;
Node* ppnode = parent->_parent;
if (subRL)
subRL->_parent = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
}
else if (parent == ppnode->_left)
{
ppnode->_left = subR;
}
else if (parent == ppnode->_right)
{
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
subL->_right = parent;
parent->_left = subLR;
Node* ppnode = parent->_parent;
parent->_parent = subL;
if (subLR)
subLR->_parent = parent;
if (parent == _root)
{
_root = subL;
}
else if (parent == ppnode->_left)
{
ppnode->_left = subL;
}
else if (parent == ppnode->_right)
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
3 迭代器的实现
这里的迭代器与链表哪里的迭代器一样,需要将节点指针进行封装,自己重载++与–运算符,因为自然的++与–运算符不符合我们的预期!
实现如下所示:
template<class T,class Ptr,class Ptr1>
struct RBIterator {
typedef RBTreeNode<T> Node;
typedef RBIterator<T,Ptr,Ptr1> self;
RBIterator(Node* node)
:_node(node)
{}
self& operator++()
{
Node* curRight = _node->_right;
if (curRight)
{
while (curRight->_left)
{
curRight = curRight->_left;
}
_node = curRight;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent&&cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
self& operator--()
{
Node* curLeft = _node->_left;
if (curLeft)
{
while (curLeft->_right)
{
curLeft = curLeft->_right;
}
_node = curLeft;
}
else
{
Node* cur = _node;
Node* parent = _node->_parent;
while (parent && parent->_left == cur)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Ptr operator*()
{
return _node->_data;
}
Ptr1 operator->()
{
return &(_node->_data);
}
bool operator==(const self& it)
{
return _node == it._node;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
private:
Node* _node;
};
这里就重点提一下++的实现:
1️⃣假设当前节点就是根节点,要对下一个节点进行访问,那么就需要我们找到右子树中的最左边的节点
2️⃣然后在判断cur与parent的位置关系,如果parent的左边为cur,那么此时的parent就是下一个要访问的节点,如果cur在parent的右边时,cur往上走,parent也会往上走,一直可能走到parent为空的情况
同理–这个运算符就与++是相反的!
4 实现map与set
map的实现
template<class K,class V>
class Map {
//仿函数 内部类取first数据
struct MapCompare {
const K& operator()(const pair<const K, V>& data)
{
return data.first;
}
};
public:
typedef typename RBTree<K,pair<const K,V>,MapCompare>::iterator iterator;
typedef typename RBTree<K, pair<const K,V>, MapCompare>::const_itertaor const_iterator;
private:
RBTree<K, pair<const K, V>, MapCompare> _t;
public:
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(pair<K,V> kv)
{
pair<iterator, bool> it = _t.Insert(kv);
return it;
}
};
set的实现
template<class K>
class Set {
//虚函数
struct SetCompare {
const K& operator()(const K& data)
{
return data;
}
};
typedef typename RBTree < K, const K, SetCompare>::iterator iterator;
typedef typename RBTree < K, const K, SetCompare>::const_itertaor const_iterator;
public:
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)
{
pair<iterator, bool> it = _t.Insert(key);
return it;
}
private:
RBTree<K,const K,SetCompare> _t;
};