1.map和set
map与set的底层是红黑树,直接在map与set中封装一棵红黑树,然后包装其接口。
2.改造红黑树
如果我们要将红黑树拿去封装map和set的话,首先需要增加迭代器,便于map与set的遍历。
template <class T,class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
__RBTreeIterator(const __RBTreeIterator<T,T&,T*>& it)
:_node(it._node)
{}
typedef __RBTreeIterator<T,Ref,Ptr> Self;
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_right)
{
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
else
{
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)
{
Node* subRight = _node->_left;
while (subRight->_right)
{
subRight = subRight->_right;
}
_node = subRight;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
};
2.1 operator++与--
operator++分为有右子树和没有右子树两种情况。顺序是中序遍历,左根右。
右不为空,下一个就是右子树的最左节点。
右为空,沿着到根的路径,找孩子是父亲左的那个祖先。
operator--则相反,分为左子树为空和左子树不为空两种情况。
左子树不为空,去找左子树的最右节点。
左子树为空,沿着到根的路径,找孩子是父亲右的那个祖先。
与中序遍历相反,右根左。
2.2 begin与end
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*> const_Iterator;
Iterator begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur);
}
Iterator end()
{
return Iterator(nullptr);
}
begin直接是左子树的最左节点,end直接给空指针。
3.map
#pragma once
#include "RBTree.h"
namespace L
{
template <class K, class V>
class map
{
struct MapKeyofT
{
const K& operator()(const pair<const K, V>& kv)
{
return kv.first;
}
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::Iterator Iterator;
Iterator begin()
{
return _t.begin();
}
Iterator end()
{
return _t.end();
}
pair<Iterator,bool> Insert(const pair<const K, V>& kv)
{
return _t.Insert(kv);
}
V& operator[](const K& key)
{
pair<Iterator, bool> ret = _t.Insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKeyofT> _t;
};
直接套用红黑树的接口。map的插入函数的返回值需要设计成pair<iterator,bool>的形式,以便于operator[]能实现插入+修改Val的功能。
4.set
#pragma once
#include "RBTree.h"
namespace L
{
template <class K>
class set
{
struct SetKeyofT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename RBTree<K, K, SetKeyofT>::const_Iterator Iterator;
typedef typename RBTree<K, K, SetKeyofT>::const_Iterator const_Iterator;
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)
{
return _t.Insert(key);
}
private:
RBTree<K, K, SetKeyofT> _t;
};
5.迭代器隐式类型转换问题
由于set中不允许修改key,所以set的迭代器都是设计成const迭代器。
typedef typename RBTree<K, K, SetKeyofT>::const_Iterator Iterator;
typedef typename RBTree<K, K, SetKeyofT>::const_Iterator const_Iterator;
而set在调用红黑树的begin时,返回的是一个普通迭代器,而普通迭代器无法转换成const迭代器。那把红黑树里的迭代器都设计成const迭代器行不行呢?答案是不行,因为map里需要普通迭代器来修改val。
typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::Iterator Iterator;
为此我们需要在红黑树迭代器里设计一个特殊的构造函数。
__RBTreeIterator(const __RBTreeIterator<T,T&,T*>& it)
:_node(it._node)
{}
当传进来的是普通迭代器时,这个构造函数就是拷贝构造。
当传进来的是const迭代器时,这个构造函数就是一个支持用普通迭代器来构造const迭代器的构造函数。这个构造函数是一个单参数的隐式类型转换构造函数,把普通迭代器转换成了模板实例化的const迭代器对象本身。