前言
红黑树性质:
- 节点是红色或黑色。
- 根是黑色。
- 所有叶子都是黑色(叶子是
NIL
节点)。
- 每个红色节点必须有两个黑色的子节点。
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
连一刻都没有为国庆假期悼念,立刻赶来战场的是 ———— 长达七天的调休。而我们也将继续 STL 的旅程,本节将要分析的是 STL 红黑树的构造以及对外接口的实现。
rb-tree 的构造与内存管理
红黑树的构造与析构函数如下:
1 2 3 4 5 6 7 8 9 10
| public: rb_tree() { rb_tree_init(); }
rb_tree(const rb_tree& rhs); rb_tree(rb_tree&& rhs) noexcept;
rb_tree& operator=(const rb_tree& rhs); rb_tree& operator=(rb_tree&& rhs) noexcept;
~rb_tree() { clear(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| template <class T, class Compare> rb_tree<T, Compare>::rb_tree(const rb_tree& rhs) { rb_tree_init(); if (rhs.node_count_ != 0) { root() = copy_from(rhs.root(), header_); leftmost() = rb_tree_min(root()); rightmost() = rb_tree_max(root()); } node_count_ = rhs.node_count_; key_comp_ = rhs.key_comp_; }
template <class T, class Compare> rb_tree<T, Compare>::rb_tree(rb_tree&& rhs) noexcept : header_(tinystl::move(rhs.header_)), node_count_(rhs.node_count_), key_comp_(rhs.key_comp_) { rhs.reset(); }
|
可以看出,除移动构造外,构造函数均调用了 rb_tree_init()
函数来初始化红黑树,让我们来看看这个函数的实现:
1 2 3 4 5 6 7 8 9 10
| template <class T, class Compare> void rb_tree<T, Compare>::rb_tree_init() { header_ = base_allocator::allocate(1); header_->color = rb_tree_red; root() = nullptr; leftmost() = header_; rightmost() = header_; node_count_ = 0; }
|
在 Container-6.2 中也分析了 STL 红黑树中的特殊设计:header
节点,该节点的存在使得我们可以很轻松的处理各种边界情况:如判断是否查找完、找最大最小节点等。
因此,维护正确的 header
对于红黑树来说就至关重要。初始化函数会生成一个 header
节点并将其染为红色,以区分根节点与 header
节点,同时令 root() = nullptr
、leftmost
与 rightmost
都指向自身。
由于 tinystl
中额外维护了 node_count
的信息,因此这里也要进行相应的初始化。
1 2 3 4 5 6
| template <class T, class Compare> void rb_tree<T, Compare>::reset() { header_ = nullptr; node_count_ = 0; }
|
在移动构造函数中用到了 reset()
这个函数,就是简单地将 header_
与 node_count_
归零,但值得注意的是,该函数只在移动构造与移动赋值中才能使用,否则会出现严重的内存泄漏问题。
1 2 3 4 5 6 7 8 9 10 11
| template <class T, class Compare> void rb_tree<T, Compare>::clear() { if (node_count_ != 0) { erase_since(root()); leftmost() = header_; root() = nullptr; rightmost() = header_; node_count_ = 0; } }
|
另外,可以看出 rb-tree
的析构函数就是调用了 clear()
函数,分析该函数的实现可以发现,该函数先析构并释放所有的节点,之后再把 root
、leftmost
、rightmost
以及 node_count_
都重置一遍,关于 erase_since(root())
的实现会在后面进行说明。
除了 rb_tree_init()
与 reset()
外,还有以下几个关于节点的构造与销毁的函数经常被使用到,这里一并列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| template <class T, class Compare> template <class ...Args> typename rb_tree<T, Compare>::node_ptr rb_tree<T, Compare>::create_node(Args&& ...args) { auto tmp = node_allocator::allocate(1); try { data_allocator::construct(tinystl::address_of(tmp->value), tinystl::forward<Args>(args)...); tmp->parent = nullptr; tmp->left = nullptr; tmp->right = nullptr; } catch (...) { node_allocator::deallocate(tmp); throw; } return tmp; }
template <class T, class Compare> typename rb_tree<T, Compare>::node_ptr rb_tree<T, Compare>::clone_node(base_ptr x) { auto tmp = create_node(static_cast<node_ptr>(x)->value); tmp->color = x->color; tmp->left = nullptr; tmp->right = nullptr; return tmp; }
template <class T, class Compare> void rb_tree<T, Compare>::destroy_node(node_ptr x) { data_allocator::destroy(tinystl::address_of(x->value)); node_allocator::deallocate(x); }
|
rb-tree 的元素插入
rb-tree
提供多种插入元素的操作,由于需要支持可重复的容器与不可重复的容器,因此需要两套不同的插入操作;同时在内部还需要处理插入元素为 pair
与普通元素各自的情况。因此较为繁琐。以下以 insert_multi()
代表可重复容器的插入操作,insert_unique()
代表不可重复容器的插入操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public:
template <class ...Args> iterator emplace_multi(Args&& ...args);
template <class ...Args> tinystl::pair<iterator, bool> emplace_unique(Args&& ...args);
template <class ...Args> iterator emplace_multi_use_hint(iterator hint, Args&& ...args);
template <class ...Args> iterator emplace_unique_use_hint(iterator hint, Args&& ...args);
iterator insert_multi(const value_type& value); iterator insert_multi(value_type&& value) { return emplace_multi(tinystl::move(value)); }
iterator insert_multi(iterator hint, const value_type& value) { return emplace_multi_use_hint(hint, value); }
iterator insert_multi(iterator hint, value_type&& value) { return emplace_multi_use_hint(hint, tinystl::move(value)); }
template <class InputIterator> void insert_multi(InputIterator first, InputIterator last) { size_type n = tinystl::distance(first, last); THROW_LENGTH_ERROR_IF(node_count_ > max_size() - n, "rb_tree<T, Comp>'s size too big"); for (; n > 0; --n, ++first) insert_multi(end(), *first); }
tinystl::pair<iterator, bool> insert_unique(const value_type& value); tinystl::pair<iterator, bool> insert_unique(value_type&& value) { return emplace_unique(tinystl::move(value)); }
iterator insert_unique(iterator hint, const value_type& value) { return emplace_unique_use_hint(hint, value); }
iterator insert_unique(iterator hint, value_type&& value) { return emplace_unique_use_hint(hint, tinystl::move(value)); }
template <class InputIterator> void insert_unique(InputIterator first, InputIterator last) { size_type n = tinystl::distance(first, last); THROW_LENGTH_ERROR_IF(node_count_ > max_size() - n, "rb_tree<T, Comp>'s size too big"); for (; n > 0; --n, ++first) insert_unique(end(), *first); }
|
插入元素的辅助函数
get_Insert_pos
以下两个函数用于获取指定 key 在红黑树中的插入位置,分为 multi 与 unique 两类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| template <class T, class Compare> tinystl::pair<typename rb_tree<T, Compare>::base_ptr, bool> rb_tree<T, Compare>::get_insert_multi_pos(const key_type& key) { auto x = root(); auto y = header_; bool add_to_left = true; while (x != nullptr) { y = x; add_to_left = key_comp_(key, value_traits::get_key(static_cast<node_ptr>(x)->value)); x = add_to_left ? x->left : x->right; } return tinystl::make_pair(y, add_to_left); }
template <class T, class Compare> tinystl::pair<tinystl::pair<typename rb_tree<T, Compare>::base_ptr, bool>, bool> rb_tree<T, Compare>::get_insert_unique_pos(const key_type& key) { auto x = root(); auto y = header_; bool add_to_left = true; while (x != nullptr) { y = x; add_to_left = key_comp_(key, value_traits::get_key(static_cast<node_ptr>(x)->value)); x = add_to_left ? x->left : x->right; } iterator it(y); if (add_to_left) { if (it == begin()) { return tinystl::make_pair(tinystl::make_pair(y, true), true); } else --it; }
if (key_comp_(value_traits::get_key(*it), key)) { return tinystl::make_pair(tinystl::make_pair(y, add_to_left), true); } return tinystl::make_pair(tinystl::make_pair(y, add_to_left), false); }
|
insert_node_at
以下的函数用于在指定的位置插入节点,值得注意的是,由于插入节点很有可能改变 root
、leftmost
、rightmost
,因此这两个函数始终要维护正确的信息。此外,该函数会调用前面分析过的 rb_tree_insert_rebalance()
来维持红黑树的平衡。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::insert_node_at(base_ptr x, node_ptr node, bool add_to_left) { node->parent = x; auto base_node = static_cast<base_ptr>(node); if (x == header_) { root() = base_node; leftmost() = base_node; rightmost() = base_node; } else if (add_to_left) { x->left = base_node; if (x == leftmost()) leftmost() = base_node; } else { x->right = base_node; if (x == rightmost()) rightmost() = base_node; } rb_tree_insert_rebalance(base_node, root()); ++node_count_; return iterator(node); }
template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::insert_value_at(base_ptr x, const value_type& value, bool add_to_left) { node_ptr node = create_node(value); node->parent = x; auto base_node = static_cast<base_ptr>(node); if (x == header_) { root() = base_node; leftmost() = base_node; rightmost() = base_node; } else if (add_to_left) { x->left = base_node; if (x == leftmost()) leftmost() = base_node; } else { x->right = base_node; if (x == rightmost()) rightmost() = base_node; } rb_tree_insert_rebalance(base_node, root()); ++node_count_; return iterator(node); }
|
插入元素
有了上面两个辅助函数的帮助,插入元素的过程就是依次调用 get_insert_pos()
与 insert_node_at()
。同时,红黑树中也有 emplace
与 insert
两种不同的插入方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| template <class T, class Compare> template <class ...Args> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::emplace_multi(Args&& ...args) { THROW_LENGTH_ERROR_IF(node_count_ > max_size() - 1, "rb_tree<T, Compare>'s size too big"); node_ptr node = create_node(tinystl::forward<Args>(args)...); auto res = get_insert_multi_pos(value_traits::get_key(node->value)); return insert_node_at(res.first, node, res.second); }
template <class T, class Compare> template <class ...Args> tinystl::pair<typename rb_tree<T, Compare>::iterator, bool> rb_tree<T, Compare>::emplace_unique(Args&& ...args) { THROW_LENGTH_ERROR_IF(node_count_ > max_size() - 1, "rb_tree<T, Compare>'s size too big"); node_ptr node = create_node(tinystl::forward<Args>(args)...); auto res = get_insert_unique_pos(value_traits::get_key(node->value));
if (res.second) { return tinystl::make_pair(insert_node_at(res.first.first, node, res.first.second), true); } destroy_node(node); return tinystl::make_pair(iterator(res.first.first), false); }
template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::insert_multi(const value_type& value) { THROW_LENGTH_ERROR_IF(node_count_ > max_size() - 1, "rb_tree<T, Compare>'s size too big"); auto res = get_insert_multi_pos(value_traits::get_key(value)); return insert_value_at(res.first, value, res.second); }
template <class T, class Compare> tinystl::pair<typename rb_tree<T, Compare>::iterator, bool> rb_tree<T, Compare>::insert_unique(const value_type& value) { THROW_LENGTH_ERROR_IF(node_count_ > max_size() - 1, "rb_tree<T, Compare>'s size too big"); auto res = get_insert_unique_pos(value_traits::get_key(value)); if (res.second) { return tinystl::make_pair(insert_value_at(res.first.first, value, res.first.second), true); } return tinystl::make_pair(res.first.first, false); }
|
rb-tree 的元素删除
删除操作相较于插入操作会简单些,但有一部分删除操作需要用到 find()
函数,会在后面讲解。
1 2 3 4 5 6 7 8 9 10
|
iterator erase(iterator hint);
size_type erase_multi(const key_type& key); size_type erase_unique(const key_type& key);
void erase(iterator first, iterator last);
void clear();
|
以下的删除节点函数基本都会调用 erase()
,在 erase()
中可以看出,就是调用了 rb_tree_erase_rebalance()
函数,再销毁被要被删除的节点,再维护好 node_count_
的信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::erase(iterator hint) { auto node = static_cast<node_ptr>(hint.node); iterator next(node); ++next; rb_tree_erase_rebalance(hint.node, root(), leftmost(), rightmost()); destroy_node(node); --node_count_; return next; }
template <class T, class Compare> typename rb_tree<T, Compare>::size_type rb_tree<T, Compare>::erase_multi(const key_type& key) { auto res = equal_range_multi(key); auto count = tinystl::distance(res.first, res.second); erase(res.first, res.second); return count; }
template <class T, class Compare> typename rb_tree<T, Compare>::size_type rb_tree<T, Compare>::erase_unique(const key_type& key) { auto it = find(key); if (it == end()) return 0; erase(it); return 1; }
template <class T, class Compare> void rb_tree<T, Compare>::erase(iterator first, iterator last) { if (first == begin() && last == end()) clear(); else { while (first != last) erase(first++); } }
template <class T, class Compare> void rb_tree<T, Compare>::clear() { if (node_count_ != 0) { erase_since(root()); leftmost() = header_; root() = nullptr; rightmost() = header_; node_count_ = 0; } }
|
值得注意的是 clear()
函数,内部调用了一个名为 erase_since()
的函数,这个函数通过递归调用的方式销毁掉以 x
为根节点的所有节点。
1 2 3 4 5 6 7 8 9 10 11
| template <class T, class Compare> void rb_tree<T, Compare>::erase_since(base_ptr x) { while (x != nullptr) { erase_since(x->right); auto y = x->left; destroy_node(static_cast<node_ptr>(x)); x = y; } }
|
rb-tree 的元素查找
由于红黑树也是一棵二叉搜索树,因此查找指定元素可以以 logn
的复杂度快速完成,除了 find()
外,rb-tree
还提供了 count
、euqal_range
等查询接口,这部分的原理十分简单,就不再赘述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public: iterator find(const key_type& key);
size_type count_multi(const key_type& key) const { auto p = equal_range_multi(key); return static_cast<size_type>(tinystl::distance(p.first, p.second)); }
size_type count_unique(const key_type& key) const { return find(key) == end() ? 0 : 1; }
iterator lower_bound(const key_type& key); iterator upper_bound(const key_type& key);
tinystl::pair<iterator, iterator> equal_range_multi(const key_type& key) { return tinystl::pair<iterator, iterator>(lower_bound(key), upper_bound(key)); }
tinystl::pair<iterator, iterator> equal_range_unique(const key_type& key) { iterator it = find(key); auto next = it; return it == end() ? tinystl::make_pair(it, it) : tinystl::make_pair(it, ++next); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::find(const key_type& key) { auto y = header_; auto x = root(); while (x != nullptr) { if (!key_comp_(value_traits::get_key(static_cast<node_ptr>(x)->value), key)) { y = x; x = x->left; } else x = x->right; } iterator res(y); if (res != end() && !key_comp_(key, value_traits::get_key(*res))) return res; return end(); }
template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::lower_bound(const key_type& key) { auto y = header_; auto x = root(); while (x != nullptr) { if (!key_comp_(value_traits::get_key(static_cast<node_ptr>(x)->value), key)) { y = x; x = x->left; } else x = x->right; } return iterator(y); }
template <class T, class Compare> typename rb_tree<T, Compare>::iterator rb_tree<T, Compare>::upper_bound(const key_type& key) { auto y = header_; auto x = root(); while (x != nullptr) { if (key_comp_(key, value_traits::get_key(static_cast<node_ptr>(x)->value))) { y = x; x = x->left; } else x = x->right; } return iterator(y); }
|
rb-tree 的复制
关于红黑树的复制操作,也是通过递归调用的方式完成,如果出现了任何错误就销毁整棵复制的树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| template <class T, class Compare> typename rb_tree<T, Compare>::base_ptr rb_tree<T, Compare>::copy_from(base_ptr x, base_ptr p) { auto top = clone_node(x); top->parent = p; try { if (x->right) top->right = copy_from(x->right, top); p = top; x = x->left; while (x != nullptr) { auto y = clone_node(x); p->left = y; y->parent = p; if (x->right) y->right = copy_from(x->right, y); p = y; x = x->left; } } catch (...) { erase_since(top); throw; } return top; }
|
总结
关于红黑树的分析到这里就告一段落了,本节对于红黑树内部一些函数的分析较为粗略,想要具体了解内部实现的读者还请阅读源码,下一节将开始分析基于红黑树的 set
、multiset
、map
、multimap
等的实现。