文章目录
- map和set的封装
- map和set的底层
- map和set的模拟实现
- insert
- iterator实现的思路
- operator++
- operator- -
- operator[ ]
map和set的封装
介绍map和set的底层实现
map和set的底层
一份模版实例化出key的rb_tree和pair<k,v>的rb_tree
rb_tree的Key和Value不是我们之前传统意义上的key/value
迭代器也是一份模版实例化出两个不同的迭代器
所以说底层上红黑树是有两份的,一份是key的,一份是key/value的
- 通过泛型的思想实现出的模版,第二个参数不是写死的,第二个模版参数是Value决定的,Value可以是key或者是pair<const key, T>,这样既可以实现key的搜索场景,也可以实现key/value的搜索场景
- 要注意一下,源码里面模板参数是用T代表value,而内部写的value_type不是我们我们日常key/value场景中说的value,源码中的value_type反而是红黑树结点中存储的真实的数据的类型
- rb_tree的第二个模版参数已经控制了红黑树节点的存储的数据类型,为什么还要写第二个模版参数?
set的两个模版参数都是一样的,都是key,insert的都是key,find和erase的也是key。但是对于map来说,insert的是pair对象,find/erase的是key。所以set为了兼容map就传了两个模版参数
map和set的模拟实现
pair的默认比较的是first,first小就小,first不小,比较second,second小就小。
insert
insert关键是要解决插入的值是Key还pair的问题
上层用仿函数实现一个类来解决下层比较的是key还是pair的问题,下层不知道T是什么,但是上层知道,如果比较的是key上层就传key,是pair就传pair
namespace wbc
{
template<class K>
class set
{
// 为了解决不知道下层T是key还是pair的逻辑
struct SetKeyOfT
{
// 仿函数可以解决
const K& operator()(const K& key)
{
return key;
}
};
public:
bool insert(const K& key)
{
return _t.Insert(key);
}
private:
RBTree<K,K,SetKeyOfT> _t;
};
}
namespace wbc
{
template<class K, class V>
class map
{
// 为了解决不知道下层T是key还是pair的逻辑
struct MapKeyOfT
{
// 仿函数可以解决
const pair<K, V>& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
private:
RBTree<K, pair<K, V>,MapKeyOfT> _t;
};
}
iterator实现的思路
- map和set的迭代器走的是中序遍历,begin()返回的是中序的第一个节点
- operator++核心是不看全局,只看局部,只关心当前局部的下一个节点是什么
- 中序访问的顺序:左子树,根,右子树。++,it当前节点已经访问完了,如果右不为空,访问右子树的最左节点。如果右为空,说明当前节点已经访问完了,子树也访问完了,就要访问该节点的祖先,并且要往上找。要找的是孩子是祖先的左边的那个祖先。
如果孩子在父亲的右,说明父亲访问完了,父亲在爷爷的左,下一个就访问爷爷。 - set的iterator也不支持修改,我们把set的第二个模板参数改成const K即可, RBTree<K,const K, SetKeyOfT> _t;
- map的iterator不支持修改key但是可以修改value,我们把map的第二个模板参数pair的第一个参
数改成const K即可, RBTree<K, pair<const K, V>,MapKeyOfT> _t;
operator++
- 右不为空,中序的下一个节点就是右子树中的最左节点
- 右为空,分为两种情况:
情况1:一直往上找直到找到父亲的左是当前节点,那么下一个节点就是这个父亲节点
情况2:就是一直找,找不到父亲的左是当前节点,也就是当前节点一直是父亲的右,直到找到父亲是空都没有找到,那么这棵树就走完了
Self operator++()
{
if (_node->_right)
{
// 如果右不为空,中序下一个要访问的节点就是最左(最小)节点
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
// 如果右为空,祖先里面的孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
operator- -
访问节点
- 右子树,根,左子树。如果左树不为空,访问左树的最右节点(最大节点)。如果左树为空,说明这棵子树访问完了,如果该节点还是父亲的左,说明也访问完了,要找到节点是父亲的右,下一个访问的节点就是父亲。如果都不存在,下一个就要访问空节点了,说明这棵树访问完了。
- operator- - 和operator++正好反过来了
// RBTree.h
Self operator--()
{
if (_node == nullptr) // --end()
{
// --end(),找到整棵树的最右节点,中序的最后一个节点
// 找最右节点
Node* mostright = _root;
// 空树(无节点)和找最右节点
while (mostright && mostright->_right)
{
mostright = mostright->_right;
}
_node = mostright;
}
else if (_node->_left)
{
// 如果左不为空,下一访问的是左树中的最右节点
Node* most = _node->_left;
while (most->_right)
{
most = most->_right;
}
_node = most;
}
else
{
// 如果左为空,下一个访问的是孩子是父亲右的,那个祖先
Node* cur = _node;
Node* parent = _node->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
// Myset.h
void Print(const set<int>& s)
{
set<int>::const_iterator it = s.end();
while (it != s.begin())
{
--it;
cout << *it << " ";
}
cout << endl;
}
// test.cpp
int main()
{
wbc::set<int> s;
s.insert(5);
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(6);
s.Print(s);
}
库里面实现的是有哨兵位的头节点,我们实现是end()是nullptr,但是也可以实现–end()的功能,无非就是要_root,去找整棵树的最右节点(最后一个节点),end()是最后一个节点的下一个节点
operator[ ]
operator[ ]直接调用insert即可
map实现operator[ ],修改insert的返回值为pair< Iterator,bool>
V& operator[](const K& key)
{
pair<iterator, bool> ret = insert({ key,V() });
return ret.first->second;
// 修改value
}
pair<Iterator,bool> Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
// return pair<Iterator,bool>(Iterator(_root,_root),true);
return { Iterator(_root,_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 { Iterator(cur,_root),false }; ;
}
}
cur = new Node(data);
Node* newnode = cur;
// 如果连续变色,cur不一定是新节点
// 如果是非空树,插入红色节点
cur->_col = RED;
if (kot(parent->_data) < kot(data))
{
parent->_right = cur;
}
else if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
// 链接父亲节点
cur->_parent = parent;
// parent是红色,出现了连续的红色节点,需要向上调整
// 调整之后cur是根,cur的parent是nullptr
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
// g
// p u
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
// 变色是为了处理连续的红节点,保证黑节点的数量不变,
// 向上继续调整是因为grandfather的节点可能是黑节点就结束,
// 可能是红节点就继续向上处理
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
// uncle不存在或uncle存在且为黑
// 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;
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
{
// uncle不存在或者存在且是黑
// g
// u p
// c
// 左单旋
if (parent->_right == cur)
{
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 {Iterator(newnode,_root),true};
}