目录
1. set 和 map 的封装
我们说过, set 和 map 底层的数据结构都是红黑树,难道说,set 需要写一颗红黑树,map 也要写一棵红黑树?
因为,set 是只有 Key 的,而 map 是需要一个键值对 ( Key - Value ) 的。
上述方法的确可以,但是太挫了,而 STL 是如何设计的呢?
set 如下:
map 如下:
我们发现,set 和 map 所用的红黑树中有一个差异:
- set 中的 value_type 就是 Key;
- map 中的 value_type 是一个 pair<const Key, T>。
换言之,第二个模板参数决定了这颗红黑树是给map还是给set用的:
- 如果第二个模板是 Key,那么这颗红黑树就是给 set 用的;
- 如果第二个模板参数是 pair,那么这颗红黑树就是给 map 用的。
有了上面的理解,我们就可以写出 set 和 map的大致框架了。
set 封装的初步框架:
namespace Xq
{
template<class K>
class set
{
private:
rb_tree<K, K> _tree;
};
}
map 封装的初步框架:
namespace Xq
{
template<class K, class V>
class map
{
private:
rb_tree<K, std::pair<K, V>> _tree;
};
}
从上面,我们可以看出,红黑树自身是不知道它的 Value 是什么的,因此,红黑树的 Value 不能写死,即不能写成 Key,也不能写成 pair,而应该用一个泛型,换言之,此时,红黑树的节点是一个泛型,既不是 Key - Key,也不是 Key - Value,如下:
template<class T>
struct rb_tree_node
{
rb_tree_node<T>* _left;
rb_tree_node<T>* _right;
rb_tree_node<T>* _parent;
T _data;
color _col;
rb_tree_node(const T& data = T())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
当走到这里之后,又出现问题了,比如我们的 find,如下:
bool find(const K& data)
{
Node* cur = _root;
while (cur)
{
if (cur->_data > data)
cur = cur->_left;
else if (cur->_data < data)
cur = cur->_right;
else
return true;
}
return false;
}
上面的 find 对于 set 来说,没有任何问题,因为此时的 _data 就是 Key,而我们的目的本来就是要用 Key 进行比较;
但是对于 map 来说,因为此时的 _data 是一个 pair,而默认情况下的 pair 的比较不是我们需要的,我们需要的是通过 Key 进行比较。
因此这里就需要让红黑树具有一个功能,获得 set 中的 Key 和获得 map 中的 pair 中的 Key,因此,此时的红黑树需要一个类模板参数,这个模板参数是一个仿函数,红黑树通过这个模板参数获得 set 中的 Key和 map 中 pair 中的 Key。
// 这了的 key_of_t 就是用来获取 Key 的
template<class K, class T, class key_of_t>
class rb_tree
{
// ...
};
当红黑树提供了这个模板参数 (key_of_t) ,set 和 map 就需要显示传递这个模板参数,同时,因为此时的 rb_tree 是一个类模板,故需要传递类型,如下:
set 如下:
namespace Xq
{
template<class K>
class set
{
public:
struct set_key_of_t
{
const K& operator(const K& key)
{
return key;
}
};
private:
rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};
}
map 如下:
namespace Xq
{
template<class K, class V>
class map
{
public:
struct map_key_of_t
{
const K& operator(const std::pair<K,V>& data)
{
return data.first;
}
};
private:
rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};
}
此时在红黑树中,就可以通过这个仿函数正确获得 Key,无论是 set 还是 map,比如 find,如下:
bool find(const K& key)
{
Node* cur = _root;
// 定义一个仿函数对象
key_of_t kot;
while (cur)
{
// 如果你是set, 直接取出 Key
// 如果你是map, 从 pair 中得到 first,也就是 Key
// 无论是set还是map, 都是通过Key进行比较的, 符合预期.
if (kot(cur->_data) > key)
cur = cur->_left;
else if (kot(cur->_data) < key)
cur = cur->_right;
else
return true;
}
return false;
}
根据上面的思路,我们可以修改一下红黑树的 insert,如下:
bool insert(const T& data)
{
if (!_root)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
else
{
Node* cur = _root;
Node* parent = nullptr;
key_of_t kot;
while (cur)
{
if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(data);
if (kot(data) > kot(parent->_data))
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
while (parent && parent->_col == RED)
{
// 这里的旋转调整就省略了
}
_root->_col = BLACK;
return true;
}
}
此时,我们就可以封装一下 set 和 map的 insert了,如下:
set 如下:
namespace Xq
{
template<class K>
class set
{
public:
struct set_key_of_t
{
const K& operator()(const K& key)
{
return key;
}
};
bool insert(const K& key)
{
return _tree.insert(key);
}
private:
rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};
}
map 如下:
namespace Xq
{
template<class K, class V>
class map
{
public:
struct map_key_of_t
{
const K& operator()(const std::pair<K,V>& data)
{
return data.first;
}
};
bool insert(const std::pair<K,V>& kv)
{
return _tree.insert(kv);
}
private:
rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};
}
走到这里,暂停一下,我们需要实现下红黑树的迭代器。
2. 红黑树的迭代器
迭代器的实现我们在 list 就已经详细论述过,因为 Node* 是一个内置类型,无法重载 ++,--等行为 (因为这些 Node* 的地址并非连续,指针的++,-- 不符合需求),而要重载 ++,--就需要是一个自定义类型,故,在这里就将一个 Node* 封装成一个类,这个类起了一个好听的名字:迭代器,这个迭代器通常需要实现以下操作:
加加 (++)、减减 (--)、解引用 (*)、访问成员 (->)、等于 (==)、不等于 (!=) 。
2.1. 普通迭代器
我们先实现一个普通迭代器,大致框架如下:
template<class T>
struct tree_iterator
{
typedef rb_tree_node<T> Node;
typedef tree_iterator<T> Self;
Node* _node;
tree_iterator(Node* node = nullptr) :_node(node){}
// 前置++
Self& operator++() {}
// 后置++
Self operator++(int) {}
// 前置--
Self& operator--() {}
// 后置--
Self operator--(int) {}
bool operator==(const Self& cmp)
{
return _node == cmp._node;
}
bool operator!=(const Self& cmp)
{
return _node != cmp._node;
}
// 返回对象的引用
T& operator*() { return _node->_data; }
// 返回对象的地址
T* operator->() { return &(operator*()) };
};
这里面最有难度的就是 ++和--操作了,我们只详细论述前置++和前置--了,理解了这些,后置++和后置--就水到渠成了。
2.1.1. 前置++
前置++的核心就是找中序的下一个节点,而中序遍历的规则是左子树、根、右子树;
我们以下面这颗红黑树举例:
而我们是如何用迭代器的呢?如下 demo:
void Test2(void)
{
Xq::set<int> s = { 25, 15, 50, 10, 22, 30, 70, 18, 45 };
Xq::set<int>::iterator it = s.begin();
while (it != s.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
}
因为是中序遍历,所以 begin() 的位置,就是这棵树的最左节点,如下所示:
那么下一个节点是什么呢? 依据左子树、根、右子树,因为此时 it 是最左节点,故它不可能有左孩子,他只可能有右孩子,但是此时没有,即右孩子为空,此时说明 it 这颗子树已经遍历完了,那么下一个位置就是:
如果右孩子为空,那么下一个位置就是,孩子是父亲的左孩子的那个父节点。
比如,此时的 it 正好是它父亲的左孩子,因此,下一个位置就是父节点自身。
依据左子树、根、右子树,当 it 所在的红黑树左子树和根都遍历完了,就需要遍历右子树了,因此,下一个位置:
如果右子树不为空,下一个位置就是右子树的最左节点。 如下:
这里有一种特殊情况,当 it 走到这里的时候,如下:
此时的 it 是这棵红黑树中最后的一个有效节点 (左子树、根、右子树),此时它的情况就是,右子树为空,那么就要找孩子是父亲的左孩子的父节点,过程如下:
因为,cur_parent->_left != cur,故继续向上遍历,cur = cur_parent,cur_parent = cur_parent->_parent,可以看出,这个过程是依赖于三叉链的,如下:
同上, cur_parent->_left != cur,继续上述过程,cur = cur_parent,cur_parent = cur_parent->_parent,但此时就出问题了,此时的 cur_parent->_parent 等于 nullptr (根节点没有父亲),因此,无法找到孩子是父亲的左孩子的那个父节点,故当 cur_parent 走到空时,结束循环,并将 nullptr 构造一个迭代器并返回,这个迭代器就是 end(),如下:
总结: 前置++的大致实现思路如下:
- 如果右子树不为空,下一个位置就是,右子树的最左节点;
- 如果右子树为空,下一个位置就是:
- 如果可以找到,孩子是父节点的左孩子,那么下一个位置就是这个父节点;
- 如果不能找到,孩子是父节点的左孩子,即 cur_parent 走到了空,那么下一个位置就是 nullptr,即 end()。
有了上面理解,代码如下:
// 前置++
Self& operator++()
{
// 如果右子树不为空, 下一个位置就是右子树的最左节点
if (_node->_right)
{
Node* left_node = _node->_right;
while (left_node->_left)
left_node = left_node->_left;
_node = left_node;
}
// 如果右子树为空
else
{
// 找孩子是父亲的左孩子的那个父节点
Node* cur = _node;
Node* cur_parent = _node->_parent;
while (cur_parent && cur_parent->_right == cur)
{
cur = cur_parent;
cur_parent = cur_parent->_parent;
}
// 走到这里有两种情况
// case 1: 找到了孩子是父亲的左孩子的父节点
// case 2: 父亲走到了空, 此时的 _node 就是 nullptr
_node = cur_parent;
}
return *this;
}
2.1.2. 前置--
对于--操作,它的遍历规则我们可以理解为右子树、根、左子树。
同理,我们也以下面这颗红黑树举例,如下:
依据右子树、根、左子树,故此时的第一个节点是这棵树的最右节点, 如下:
因为此时的 it 是树的最右节点,故不存在右孩子,只可能存在左孩子,但此时的左孩子为空。
如果一个节点的左子树为空,那么下一个位置就是,孩子是父亲的右孩子的那个父节点。
对于上面来说,此时的 it 正好是父亲的右孩子,故下一个位置如下所示:
依据右子树、根、左子树判定,接下来就要访问左子树了,因此:
如果一个节点的左子树不为空,那么下一个位置就是左子树的最右节点。 如下所示:
对于 -- 仍存在一种特殊情况,当 it 走到了这个位置,如下所示:
此时的 it 是这棵红黑树中最后的一个有效节点 (右子树、根、左子树),此时它的情况就是,左子树为空,那么就要找孩子是父亲的右孩子的父节点,过程如下:
因为此时 cur_parent->_right != cur,故向上遍历,即 cur = cur_parent; cur_parent = cur_parent->_parent;
如下所示:
同上,继续向上遍历,如下:
总结: 前置--的大致实现思路如下:
- 如果左子树不为空,下一个位置就是,左子树的最右节点;
- 如果左子树为空,下一个位置就是:
- 如果可以找到,孩子是父节点的右孩子,那么下一个位置就是这个父节点;
- 如果不能找到,孩子是父节点的右孩子,即 cur_parent 走到了空,那么下一个位置就是 nullptr。
有了上面理解,代码如下:
// 前置--
Self& operator--()
{
// 依据的思想 右子树、根、左子树.
// 如果左子树不为空, 找左子树的最右节点
if (_node->_left)
{
Node* right_node = _node->_left;
while (right_node->_right)
right_node = right_node->_right;
_node = right_node;
}
// 如果左子树为空, 找孩子是父亲的右孩子的那个父亲
else
{
Node* cur = _node;
Node* cur_parent = cur->_parent;
while (cur_parent && cur_parent->_left)
{
cur = cur_parent;
cur_parent = cur_parent->_parent;
}
_node = cur_parent;
}
return *this;
}
2.1.3. 普通迭代器的完整实现
有了这些,剩下就简单了,普通迭代器的实现如下:
template<class T>
struct tree_iterator
{
typedef rb_tree_node<T> Node;
typedef tree_iterator<T> Self;
Node* _node;
tree_iterator(Node* node = nullptr) :_node(node){}
// 前置++
Self& operator++()
{
// 如果右子树不为空, 下一个位置就是右子树的最左节点
if (_node->_right)
{
Node* left_node = _node->_right;
while (left_node->_left)
left_node = left_node->_left;
_node = left_node;
}
// 如果右子树为空
else
{
// 找孩子是父亲的左孩子的那个父节点
Node* cur = _node;
Node* cur_parent = _node->_parent;
while (cur_parent && cur_parent->_right == cur)
{
cur = cur_parent;
cur_parent = cur_parent->_parent;
}
// 走到这里有两种情况
// case 1: 找到了孩子是父亲的左孩子的父节点
// case 2: 父亲走到了空, 此时的 _node 就是 nullptr
_node = cur_parent;
}
return *this;
}
// 后置++
Self operator++(int) {}
// 前置--
Self& operator--()
{
// 依据的思想 右子树、根、左子树.
// 如果左子树不为空, 找左子树的最右节点
if (_node->_left)
{
Node* right_node = _node->_left;
while (right_node->_right)
right_node = right_node->_right;
_node = right_node;
}
// 如果左子树为空, 找孩子是父亲的右孩子的那个父亲
else
{
Node* cur = _node;
Node* cur_parent = cur->_parent;
while (cur_parent && cur_parent->_left)
{
cur = cur_parent;
cur_parent = cur_parent->_parent;
}
_node = cur_parent;
}
return *this;
}
// 后置--
Self operator--(int) {}
bool operator==(const Self& cmp)
{
return _node == cmp._node;
}
bool operator!=(const Self& cmp)
{
return _node != cmp._node;
}
// 返回对象的引用
T& operator*()
{
return _node->_data;
}
// 返回对象的地址
T* operator->()
{
return &(operator*());
};
};
实现了迭代器后, 红黑树自身也需要提供一些接口,如下:
template<class K, class T, class key_of_t>
class rb_tree
{
private:
typedef rb_tree_node<T> Node;
public:
typedef tree_iterator<T> iterator;
iterator begin()
{
Node* cur = _root;
while (cur->_left)
cur = cur->_left;
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
// ... 省略
private:
Node* _root;
}
此时 set 和 map 就可以使用这个迭代器了。
set 如下:
template<class K>
class set
{
public:
struct set_key_of_t
{
const K& operator()(const K& key)
{
return key;
}
};
// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
typedef typename rb_tree<K, K, set_key_of_t>::iterator iterator;
iterator begin()
{
return _tree.begin();
}
iterator end()
{
return _tree.end();
}
}
private:
rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};
map 如下:
template<class K, class V>
class map
{
public:
struct map_key_of_t
{
const K& operator()(const std::pair<K,V>& data)
{
return data.first;
}
};
// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
typedef typename rb_tree<K, std::pair<K, V>, map_key_of_t>::iterator iterator;
iterator begin()
{
return _tree.begin();
}
iterator end()
{
return _tree.end();
}
private:
rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};
2.2. const 迭代器
在这里提示一下,这里是在标题3和标题4的基础之上进行的。
有了上面的基础,我们实现 const 迭代器就简单了,如下:
template<class T, class Ref, class Ptr>
struct tree_iterator
{
typedef rb_tree_node<T> Node;
typedef tree_iterator<T, Ref, Ptr> Self;
Node* _node;
tree_iterator(Node* node = nullptr) :_node(node){}
Self& operator++() { /*省略*/}
Self operator++(int) {}
Self& operator--() { /*省略*/}
Self operator--(int) {}
bool operator==(const Self& cmp) { /*省略*/}
bool operator!=(const Self& cmp) { /*省略*/}
Ref operator*() { /*省略*/}
Ptr operator->() { /*省略*/}
};
template<class K, class T, class key_of_t>
class rb_tree
{
private:
typedef rb_tree_node<T> Node;
public:
// 如果你是普通迭代器, 那么 Ref 就是 T&, Ptr 就是 T*
typedef tree_iterator<T, T&, T*> iterator;
// const 迭代器, 那么 Ref 就是 const T&, Ptr 就是 const T*
typedef tree_iterator<T, const T&, const T*> const_iterator;
iterator begin()
{
Node* cur = _root;
while (cur->_left)
cur = cur->_left;
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin() const
{
Node* cur = _root;
while (cur->_left)
cur = cur->_left;
return const_iterator(cur);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
// 省略 ...
private:
Node* _root;
};
那么 set 和 map 的const 迭代器如何实现呢?
对于 set 而言,它的 Key 不可以修改。
对于 map 而言,Key 不可以修改,但是 Value 可以修改。
SGI-STL 中的 set 的迭代器如下:
SGI-STL 中的 map 的迭代器如下:
可以看到, set 的普通迭代器和 const 迭代器都是 const 迭代器, map 的普通迭代器就是普通迭代器, const 迭代器是 const 迭代器。
2.2.1. set 的 const 迭代器
我们按照STL的实现思路,将 set 的普通迭代器和const迭代器都设置为const迭代器,如下:
template<class K>
class set
{
public:
struct set_key_of_t
{
const K& operator()(const K& key) { return key; }
};
// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
// 普通迭代器也是const迭代器
typedef typename rb_tree<K, K, set_key_of_t>::const_iterator iterator;
// const迭代器
typedef typename rb_tree<K, K, set_key_of_t>::const_iterator const_iterator;
// 对于 set 而言, 这里只需要提供一个返回const迭代器的begin和end就可以了
const_iterator begin() const { return _tree.begin(); }
iterator end() const { return _tree.end(); }
std::pair<iterator, bool> insert(const K& key)
{
return _tree.insert(key);
}
private:
rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};
通过下面的 demo 测试上面有没有问题:
void Test2(void)
{
int a[] = { 25, 15, 50, 10, 22, 30, 70, 18, 45 };
Xq::set<int> s;
for (auto & it : a)
s.insert(it);
Xq::set<int>::iterator it = s.begin();
while (it != s.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
}
编译代码,得到如下问题:
原因如下:
此时Xq::rb::insert 返回值中的iterator 和 Xq::set::insert 返回值中的 iterator 并不是一个类型,此时是两个不相干的类型,无法直接转换。
在这里的一个简单解决方案就是更改 Xq::rb_tree::insert,pair 的first 不要固定为迭代器,而是固定为一个节点,如下所示:
std::pair<Node*, bool> insert(const T& data)
{
if (!_root)
{
_root = new Node(data);
_root->_col = BLACK;
return {_root, true};
}
else
{
Node* cur = _root;
Node* parent = nullptr;
key_of_t kot;
while (cur)
{
if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return{ nullptr, false };
}
}
cur = new Node(data);
// 由于下面的旋转可能会更改cur, 因此提前保存一下
Node* RetNode = cur;
if (kot(data) > kot(parent->_data))
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
while (parent && parent->_col == RED){ // 旋转逻辑省略 }
_root->_col = BLACK;
return std::pair<Node*, bool>(RetNode, true);
}
}
此时在 Xq::set::insert 中,返回的 pair 是一个 std::pair<Node*, bool>;
std::pair<iterator, bool> insert(const K& key)
{
// 返回的是一个 std::pair<Node*, bool>
// 此时会调用 std::pair 的拷贝构造
// 对于pair的first, 会调用 iterator 的构造
// 这里的 iterator 实际上就是 const_iterator
return _tree.insert(key);
}
此时就解决了这个问题,这是一种很巧妙的方式,这种方案的基础是 std::pair 提供了一个拷贝构造,如下:
template<class T1, class T2>
struct pair
{
template<class U, class V>
pair(const pair<U, V>& pr)
:first(pt.first)
, second(pt.second)
{}
T1 first;
T2 second;
};
如果U的类型就是T1的类型,V的类型就是T2的类型,那么初始化列表中就是拷贝构造;
如果他们类型不匹配,那么初始化列表中就是构造。
就例如,上面的insert:
- Xq::rb_tree::insert 中的返回值类型为 pair<Node*, bool>;
- Xq::set::insert 中的返回值类型为 pair<iterator, bool>;
此时两个pair的first的类型就不一致,此时在初始化列表中就会调用构造函数,即用一个 Node* 构造一个迭代器。
2.2.2. map 的 const 迭代器
class map
{
public:
struct map_key_of_t
{
const K& operator()(const std::pair<K,V>& data)
{
return data.first;
}
};
// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
// 普通迭代器
typedef typename rb_tree<K, std::pair<K, V>, map_key_of_t>::iterator iterator;
// const迭代器
typedef typename rb_tree<K, std::pair<K, V>, map_key_of_t>::const_iterator const_iterator;
iterator begin() { return _tree.begin(); }
iterator end() { return _tree.end(); }
const_iterator begin() const { return _tree.begin(); }
const_iterator end() const { return _tree.end(); }
std::pair<iterator, bool> insert(const std::pair<K, V>& kv)
{
return _tree.insert(kv);
}
V& operator[](const K& key)
{
std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
}
private:
rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};
而 map 需要注意一个问题,它的Key是不能修改的,但是Value是可以修改的,这该如何实现呢? 很简单,只需要在实例化类模板的时候,用const修饰K就可以了 (std::pair中的Key),如下:
class map
{
public:
// 在实例化类模板的时候, 用const修饰Key
typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::iterator iterator;
// 在实例化类模板的时候, 用const修饰Key
typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::const_iterator const_iterator;
// 省略
private:
// 在实例化类模板的时候, 用const修饰Key
rb_tree<K, std::pair<const K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};
3. insert 的实现
上面我们已经完成了迭代器的实现,有了迭代器,我们就需要重新调整一下 insert 的返回值了。
// std::set::insert
pair<iterator,bool> insert (const value_type& val);
// std::map::insert
pair<iterator,bool> insert (const value_type& val);
红黑树的 insert,更改如下:
// Xq::rb_tree::insert
std::pair<iterator, bool> insert(const T& data)
{
if (!_root)
{
_root = new Node(data);
_root->_col = BLACK;
return {iterator(_root), true};
}
else
{
Node* cur = _root;
Node* parent = nullptr;
key_of_t kot;
while (cur)
{
if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return{ iterator(nullptr), false };
}
}
cur = new Node(data);
// 由于下面的旋转可能会更改cur, 因此提前保存一下
Node* RetNode = cur;
if (kot(data) > kot(parent->_data))
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
// 当父节点parent的颜色为红色时,需要调整
while (parent && parent->_col == RED)
{
// 旋转逻辑直接干掉
}
_root->_col = BLACK;
return std::pair<iterator, bool>(iterator(RetNode), true);
}
}
set 中的 insert,如下:
std::pair<iterator, bool> insert(const K& key)
{
return _tree.insert(key);
}
map 中的 insert,如下:
std::pair<iterator, bool> insert(const std::pair<K, V>& kv)
{
return _tree.insert(kv);
}
4. map 中的operator[]
有了 insert 的实现,operator[] 就手到擒来了,因为 operator[] 返回的是 Value 的引用,如下所示:
V& operator[](const K& key)
{
std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
// ret.first 返回的是一个迭代器, eg. iterator(Node*)
// ret.first-> 这里实际上是有两个 -> , 编译器为了可读性, 省去了一个 ->
// 第一个 -> 在调用 operator-> , 返回数据的指针, 即返回的是 pair<K,V>*
// 第二个 -> 访问pair的second成员, 即Value
return ret.first->second;
}
5. 完整的封装
5.1. set 的封装
#ifndef _MYSET_HPP_
#define _MYSET_HPP_
#include "MyRBTree.hpp"
namespace Xq
{
template<class K>
class set
{
public:
struct set_key_of_t
{
const K& operator()(const K& key) { return key; }
};
// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
typedef typename rb_tree<K, K, set_key_of_t>::const_iterator iterator;
typedef typename rb_tree<K, K, set_key_of_t>::const_iterator const_iterator;
// 对于 set 而言, 这里只需要提供一个返回const迭代器的begin和end就可以了
const_iterator begin() const { return _tree.begin(); }
iterator end() const { return _tree.end(); }
std::pair<iterator, bool> insert(const K& key)
{
return _tree.insert(key);
}
private:
rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};
}
#endif
5.2. map 的封装
#ifndef _MYMAP_HPP_
#define _MYMAP_HPP_
#include "MyRBTree.hpp"
namespace Xq
{
template<class K, class V>
class map
{
public:
struct map_key_of_t
{
const K& operator()(const std::pair<K,V>& data) { return data.first; }
};
// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
// 普通迭代器
typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::iterator iterator;
// const迭代器
typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::const_iterator const_iterator;
iterator begin() { return _tree.begin(); }
iterator end() { return _tree.end(); }
const_iterator begin() const { return _tree.begin(); }
const_iterator end() const { return _tree.end(); }
std::pair<iterator, bool> insert(const std::pair<K, V>& kv) { return _tree.insert(kv); }
V& operator[](const K& key)
{
std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
// ret.first 返回的是一个迭代器, eg. iterator(Node*)
// ret.first-> 这里实际上是有两个 -> , 编译器为了可读性, 省去了一个 ->
// 第一个 -> 在调用 operator-> , 返回数据的指针, 即返回的是 pair<K,V>*
// 第二个 -> 访问pair的second成员, 即Value
return ret.first->second;
}
private:
rb_tree<K, std::pair<const K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};
}
#endif
5.3. 底层红黑树
#ifndef _MYRBTREE_HPP_
#define _MYRBTREE_HPP_
#include <iostream>
#include <utility>
#include <assert.h>
#include <queue>
#include <vector>
#include <time.h>
namespace Xq
{
enum color
{
RED,
BLACK
};
template<class T>
struct rb_tree_node
{
rb_tree_node<T>* _left;
rb_tree_node<T>* _right;
rb_tree_node<T>* _parent;
T _data;
color _col;
rb_tree_node(const T& data = T())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
template<class T, class Ref, class Ptr>
struct tree_iterator
{
typedef rb_tree_node<T> Node;
typedef tree_iterator<T, Ref, Ptr> Self;
Node* _node;
tree_iterator(Node* node = nullptr) :_node(node){}
// 前置++
Self& operator++()
{
// 如果右子树不为空, 下一个位置就是右子树的最左节点
if (_node->_right)
{
Node* left_node = _node->_right;
while (left_node->_left)
left_node = left_node->_left;
_node = left_node;
}
// 如果右子树为空
else
{
// 找孩子是父亲的左孩子的那个父节点
Node* cur = _node;
Node* cur_parent = _node->_parent;
while (cur_parent && cur_parent->_right == cur)
{
cur = cur_parent;
cur_parent = cur_parent->_parent;
}
// 走到这里有两种情况
// case 1: 找到了孩子是父亲的左孩子的父节点
// case 2: 父亲走到了空, 此时的 _node 就是 nullptr
_node = cur_parent;
}
return *this;
}
// 后置++
Self operator++(int) {}
// 前置--
Self& operator--()
{
// 依据的思想 右子树、根、左子树.
// 如果左子树不为空, 找左子树的最右节点
if (_node->_left)
{
Node* right_node = _node->_left;
while (right_node->_right)
right_node = right_node->_right;
_node = right_node;
}
// 如果左子树为空, 找孩子是父亲的右孩子的那个父亲
else
{
Node* cur = _node;
Node* cur_parent = cur->_parent;
while (cur_parent && cur_parent->_left)
{
cur = cur_parent;
cur_parent = cur_parent->_parent;
}
_node = cur_parent;
}
return *this;
}
// 后置--
Self operator--(int) {}
bool operator==(const Self& cmp)
{
return _node == cmp._node;
}
bool operator!=(const Self& cmp)
{
return _node != cmp._node;
}
// 返回对象的引用
Ref operator*()
{
return _node->_data;
}
// 返回对象的地址
Ptr operator->()
{
return &(operator*());
};
};
template<class K, class T, class key_of_t>
class rb_tree
{
private:
typedef rb_tree_node<T> Node;
public:
typedef tree_iterator<T, T&, T*> iterator;
typedef tree_iterator<T, const T&, const T*> const_iterator;
rb_tree(Node* root = nullptr) :_root(root){}
std::pair<Node*, bool> insert(const T& data)
{
if (!_root)
{
_root = new Node(data);
_root->_col = BLACK;
return{ _root, true };
}
else
{
Node* cur = _root;
Node* parent = nullptr;
key_of_t kot;
while (cur)
{
if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else
{
return{ nullptr, false };
}
}
cur = new Node(data);
// 由于下面的旋转可能会更改cur, 因此提前保存一下
Node* RetNode = cur;
if (kot(data) > kot(parent->_data))
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
// 当父节点parent的颜色为红色时,需要调整
while (parent && parent->_col == RED)
{
// 因为父节点parent->_col == RED,那么说明parent一定有父节点
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// case 1: 当父节点为红,且叔叔不为空且为红
// Solution : 变色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
if (grandfather == _root)
grandfather->_col = BLACK;
// 继续向上判断,如果依旧符合case 1,继续变色
cur = grandfather;
parent = cur->_parent;
continue;
}
// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上
// Solution: 单旋, 在这里就是对grandfather右单旋
else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK))
{
// g p
// p u ---> c g
// c u
right_rotate(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
// 旋转完,更新完颜色,调整就结束
break;
}
// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上
// Solution: 双旋,在这里就是先对p进行左单旋,在对g进行右单旋
else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK))
{
// g 先对p进行左单旋 g 在对g进行右单旋 c
// p u ---> c u ---> p g
// c p u
left_rotate(parent);
right_rotate(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
// 旋转完,更新完颜色,调整就结束
break;
}
else
{
// 非法情况
assert(false);
}
}
// 当parent是grandfather的右孩子,那么uncle就是grandfather的左孩子
else
{
Node* uncle = grandfather->_left;
// case 1: 当父节点为红,且叔叔不为空且为红
// Solution : 变色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
if (grandfather == _root)
grandfather->_col = BLACK;
// 继续向上判断,如果依旧符合case 1,继续变色
cur = grandfather;
parent = cur->_parent;
continue;
}
// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上
// Solution: 单旋, 在这里就是对grandfather左单旋
else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK))
{
// g 对g进行左单旋 p
// u p ---> g c
// c u
left_rotate(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
//旋转并将颜色更新后,就退出调整
break;
}
// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上
// Solution: 双旋,在这里就是先对p进行右单旋,在对g进行左单旋
else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK))
{
// g 先对p进行右单旋 g 在对g进行左单旋 c
// u p ---> u c ---> g p
// c p u
right_rotate(parent);
left_rotate(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
break;
}
else
{
// 非法情况,断死
assert(false);
}
}
}
_root->_col = BLACK;
return std::pair<Node*, bool>(RetNode, true);
}
}
void level_order()
{
_level_order(_root);
}
bool is_balance_tree()
{
if (_root->_col != BLACK)
{
std::cout << "根节点是红色的,异常" << std::endl;
return false;
}
return _is_balance_tree(_root);
}
int get_rb_tree_high()
{
return _get_rb_tree_high(_root);
}
iterator begin()
{
Node* cur = _root;
while (cur->_left)
cur = cur->_left;
return iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin() const
{
Node* cur = _root;
while (cur->_left)
cur = cur->_left;
return const_iterator(cur);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
private:
void _level_order(Node* root)
{
if (!root)
return;
else
{
std::queue<Node*> qu;
qu.push(root);
while (!qu.empty())
{
Node* front = qu.front();
qu.pop();
if (front)
{
qu.push(front->_left);
qu.push(front->_right);
}
if (!front)
std::cout << "N ";
else
std::cout << front->_kv.first << " ";
}
std::cout << std::endl;
}
}
void left_rotate(Node* parent)
{
Node* cur = parent;
Node* cur_right = cur->_right;
Node* cur_right_left = cur_right->_left;
Node* cur_parent = cur->_parent;
cur->_right = cur_right_left;
if (cur_right_left)
cur_right_left->_parent = cur;
cur_right->_left = cur;
cur->_parent = cur_right;
if (!cur_parent)
{
cur_right->_parent = nullptr;
_root = cur_right;
}
else
{
if (cur_parent->_left == cur)
cur_parent->_left = cur_right;
else
cur_parent->_right = cur_right;
cur_right->_parent = cur_parent;
}
}
void right_rotate(Node* parent)
{
Node* cur = parent;
Node* cur_left = cur->_left;
Node* cur_left_right = cur_left->_right;
Node* cur_parent = cur->_parent;
cur->_left = cur_left_right;
if (cur_left_right)
cur_left_right->_parent = cur;
cur_left->_right = cur;
cur->_parent = cur_left;
if (!cur_parent)
{
cur_left->_parent = nullptr;
_root = cur_left;
}
else
{
key_of_t kot;
if (kot(cur_parent->_data) > kot(cur_left->_data))
cur_parent->_left = cur_left;
else
cur_parent->_right = cur_left;
cur_left->_parent = cur_parent;
}
}
bool _is_balance_tree(Node* root)
{
if (!root)
return true;
else
{
int basic_value = _get_black_node_num(root);
return _is_check_rb_tree(root, 0, basic_value);
}
}
bool _is_check_rb_tree(Node* root, int black_num, int basic_value)
{
// basic_value: 红黑树的一条路径中的黑色节点个数,在这里作为基本值
if (root == nullptr)
{
if (black_num != basic_value)
{
std::cout << "路径中的黑色节点个数不相等,异常" << std::endl;
return false;
}
else
return true;
}
else
{
if (root->_col == BLACK)
++black_num;
// 如果一个节点是红色的,那么该节点一定不是根,因此一定有父亲
// 如果这个节点的父亲也是红色的,那么就说明出现异常
if (root->_col == RED && root->_parent->_col == RED)
{
std::cout << "child: " << root->_kv.first << "parent: " << root->_parent->_kv.first << "出现了连续的红色节点,异常" << std::endl;
return false;
}
return _is_check_rb_tree(root->_left, black_num, basic_value)
&& _is_check_rb_tree(root->_right, black_num, basic_value);
}
}
int _get_black_node_num(Node* root)
{
if (root == nullptr)
return 0;
else
{
int ret = 0;
while (root)
{
if (root->_col == BLACK)
++ret;
root = root->_left;
}
return ret;
}
}
int _get_rb_tree_high(Node* root)
{
if (root == nullptr)
return 0;
else
{
int left_high = _get_rb_tree_high(root->_left);
int right_high = _get_rb_tree_high(root->_right);
return left_high > right_high ? ++left_high : ++right_high;
}
}
private:
Node* _root;
};
}
#endif
set 和 map的封装至此结束。