अंतिम अद्यतन:2026-10-12
STL रेड-ब्लैक ट्री कार्यान्वयन¶
0.प्रस्तावना¶
STL सोर्स कोड में दो अनुच्छेद हैं, जिनका संक्षिप्त अनुवाद इस प्रकार है:
STL में Red-black tree (रेड-ब्लैक ट्री) class का उपयोग STL के असोसिएटिव कंटेनरों (जैसे set, multiset, map, multimap) के रूप में होता है। इसमें प्रयुक्त insertion तथा deletion विधियाँ “Introduction to Algorithms” पुस्तक पर आधारित हैं, परंतु निम्नलिखित दो अंतरों के साथ:
(1) header केवल root की ही नहीं, रेड-ब्लैक ट्री के सुदूर बाएँ (leftmost) नोड की भी ओर इशारा करता है, ताकि begin() निरंतर समय में कार्यान्वित हो सके; तथा वह पेड़ के सुदूर दाएँ (rightmost) नोड की ओर भी इशारा करता है, ताकि set संबंधी जेनेरिक एल्गोरिद्म (जैसे set_union इत्यादि) को रैखिक समय का प्रदर्शन मिल सके।
(2) जब हटाए जाने वाले नोड के दो बच्चे हों, तो उसका उत्तराधिकारी नोड उसके स्थान पर जुड़ जाता है, कॉपी नहीं किया जाता; अतः केवल वही इटरेटर अमान्य होता है जो हटाए गए नोड को रेफर करता है।
उपरोक्त बातों को चित्र में अनूदित करें तो नीचे जैसा है — साधारण रेड-ब्लैक ट्री की तुलना में इसमें एक अतिरिक्त header नोड है, और वह लाल है। साधारण रेड-ब्लैक ट्री 100 नोड से शुरू होता है तथा निम्नलिखित पाँच गुणधर्मों को संतुष्ट करता है: - प्रत्येक नोड या तो लाल है, या काला। - रूट नोड काला है। - प्रत्येक पत्ता नोड (NULL) काला है। - यदि कोई नोड लाल है, तो उसके दोनों बच्चे नोड काले होते हैं। - प्रत्येक नोड के लिए, उस नोड से उसके सभी वंशज पत्ता नोड्स तक के प्रत्येक सरल पथ पर काले नोड्स की संख्या समान होती है।
बेशक यहाँ rb_tree भी इन्हीं गुणधर्मों को संतुष्ट करता है; इटरेटर का begin रेड-ब्लैक ट्री के रूट नोड — अर्थात् header के पिता — की ओर इशारा करता है, जबकि end, header नोड की ओर।
।
चित्र में लोप-चिह्न यह दर्शाते हैं कि सभी नोड्स नहीं बनाए गए — अन्य नोड्स भी हैं, इसीलिए छोड़ दिए गए हैं।
1.रेड-ब्लैक ट्री नोड की बेस क्लास¶
रेड-ब्लैक ट्री की बेस क्लास बहुत सरल है; फ़ाइल के आरंभ में रंग-चिह्न परिभाषित किया गया है।
बेस क्लास में स्वयं की ओर इशारा करने वाले पॉइंटर शामिल हैं — left, right तथा parent — के साथ एक रंग-चिह्न स्थिरांक भी है। इसमें दो केंद्रीय फंक्शन्स हैं जिनका उद्देश्य रेड-ब्लैक ट्री का न्यूनतम तथा अधिकतम नोड प्राप्त करना है। हम जानते हैं कि बाइनरी सर्च ट्री में न्यूनतम नोड पाने के लिए बाएँ सबट्री में लगातार नीचे खोजना होता है, तथा अधिकतम नोड के लिए दाएँ सबट्री में लगातार नीचे खोजना होता है।
// 颜色标记
enum _Rb_tree_color { _S_red = false, _S_black = true };
// 基类
struct _Rb_tree_node_base
{
// typedef重命名
typedef _Rb_tree_node_base* _Base_ptr;
// 颜色
_Rb_tree_color _M_color;
// 指向父亲
_Base_ptr _M_parent;
// 指向左孩子
_Base_ptr _M_left;
// 指向右孩子
_Base_ptr _M_right;
// 求红黑树的最小节点
static _Base_ptr
_S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
{
while (__x->_M_left != 0) __x = __x->_M_left;
return __x;
}
// 求红黑树最大节点
static _Base_ptr
_S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT
{
while (__x->_M_right != 0) __x = __x->_M_right;
return __x;
}
};
2.रेड-ब्लैक ट्री नोड¶
रेड-ब्लैक ट्री नोड, रेड-ब्लैक ट्री की बेस क्लास से इनहेरिट करता है।
template<typename _Val>
struct _Rb_tree_node : public _Rb_tree_node_base
{
typedef _Rb_tree_node<_Value>* _Link_type;//节点指针,指向数据节点
_Value _M_value_field;//节点数据域,即关键字
};
3.रेड-ब्लैक ट्री इटरेटर¶
रेड-ब्लैक ट्री इटरेटर में रेड-ब्लैक ट्री बेस क्लास का एक मेंबर है, और उसी मेंबर के माध्यम से इटरेटर संबंधी ऑपरेशन किए जाते हैं।
साथ ही, हम जान सकते हैं कि यह इटरेटर bidirectional_iterator_tag श्रेणी का है।
इसमें traits मशीन संबंधी आवश्यक typedef भी शामिल हैं।
template<typename _Tp>
struct _Rb_tree_iterator
{
typedef _Tp value_type;
typedef _Tp& reference;
typedef _Tp* pointer;
typedef bidirectional_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
typedef _Rb_tree_iterator<_Tp> _Self;
typedef _Rb_tree_node_base::_Base_ptr _Base_ptr;
typedef _Rb_tree_node<_Tp>* _Link_type;
_Base_ptr _M_node;
};
डेटा प्राप्त करना
reference
operator*() const _GLIBCXX_NOEXCEPT
{ return *static_cast<_Link_type>(_M_node)->_M_valptr(); }
pointer
operator->() const _GLIBCXX_NOEXCEPT
{ return static_cast<_Link_type> (_M_node)->_M_valptr(); }
++ ऑपरेटर का ओवरलोड
और_Rb_tree_increment के अंतर्निहित स्तर पर local_Rb_tree_increment है, जिसका कार्यान्वयन इस प्रकार है:
static _Rb_tree_node_base *
local_Rb_tree_increment( _Rb_tree_node_base* __x ) throw ()
{
if ( __x->_M_right != 0 ) /* 存在右子树,那么下一个节点为右子树的最小节点 */
{
__x = __x->_M_right;
while ( __x->_M_left != 0 )
__x = __x->_M_left;
}else {
/* 不存在右子树,那么分为两种情况:自底往上搜索,当前节点为父节点的左孩子的时候,父节点就是后继节点; */
/* 第二种情况:_x为header节点了,那么_x就是最后的后继节点. 简言之_x为最小节点且往上回溯,一直为父节点的右孩子,直到_x变为父节点,_y为其右孩子 */
_Rb_tree_node_base *__y = __x->_M_parent;
while ( __x == __y->_M_right )
{
__x = __y;
__y = __y->_M_parent;
}
if ( __x->_M_right != __y )
__x = __y;
}
return (__x);
}
_Rb_tree_decrement के अंतर्निहित स्तर पर local_Rb_tree_decrement है, जिसका कार्यान्वयन इस प्रकार है:
static _Rb_tree_node_base *
local_Rb_tree_decrement( _Rb_tree_node_base * __x )
throw ()
{
/* header节点 */
if ( __x->_M_color ==
_S_red
&& __x
->_M_parent->_M_parent == __x )
__x = __x->_M_right;
else if ( __x->_M_left != 0 ) /* 左节点不为空,返回左子树中最大的节点 */
{
_Rb_tree_node_base *__y = __x->_M_left;
while ( __y->_M_right != 0 )
__y = __y->_M_right;
__x = __y;
}else {
/* 自底向上找到当前节点为其父节点的右孩子,那么父节点就是前驱节点 */
_Rb_tree_node_base *__y = __x->_M_parent;
while ( __x == __y->_M_left )
{
__x = __y;
__y = __y->_M_parent;
}
__x = __y;
}
return
(__x);
}
== तथा != ऑपरेटरों का ओवरलोड — सीधे नोड पॉइंटर्स की समानता जाँची जाती है।
bool
operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT
{ return _M_node == __x._M_node; }
bool
operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT
{ return _M_node != __x._M_node; }
अन्य महत्वपूर्ण फंक्शन: काले नोड्स की गणना:
unsigned int
_Rb_tree_black_count(const _Rb_tree_node_base *__node,
const _Rb_tree_node_base *__root) throw() {
if (__node == 0)
return 0;
unsigned int __sum = 0;
do {
if (__node->_M_color == _S_black)
++__sum;
if (__node == __root)
break;
__node = __node->_M_parent;
} while (1);
return __sum;
}
4.रेड-ब्लैक ट्री ऑपरेशन¶
अपेक्षाकृत महत्वपूर्ण बात — इसमें नोड बेस क्लास का उपयोग करके एक पॉइंटर घोषित किया गया है। इसमें एक _Rb_tree_impl भी शामिल है जो रेड-ब्लैक ट्री के आरंभीकरण तथा मेमोरी प्रबंधन ऑपरेशन्स के लिए है। इसमें दो प्रकार के इटरेटर भी हैं — एक rbtree का, दूसरा reverse — जो दर्शाता है कि rbegin, rend ऑपरेशन्स समर्थित हैं।
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc = allocator<_Val> >
class _Rb_tree
{
protected:
typedef _Rb_tree_node_base* _Base_ptr;
template<typename _Key_compare,
bool _Is_pod_comparator = __is_pod(_Key_compare)>
struct _Rb_tree_impl : public _Node_allocator
{
_Key_compare _M_key_compare;
_Rb_tree_node_base _M_header;
size_type _M_node_count; // Keeps track of size of tree.
_Rb_tree_impl()
: _Node_allocator(), _M_key_compare(), _M_header(),
_M_node_count(0)
{ _M_initialize(); }
_Rb_tree_impl(const _Key_compare& __comp, const _Node_allocator& __a)
: _Node_allocator(__a), _M_key_compare(__comp), _M_header(),
_M_node_count(0)
{ _M_initialize(); }
private:
void
_M_initialize()
{
this->_M_header._M_color = _S_red;
this->_M_header._M_parent = 0;
this->_M_header._M_left = &this->_M_header;
this->_M_header._M_right = &this->_M_header;
}
};
public:
typedef _Rb_tree_iterator<value_type> iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
private:
_Rb_tree_impl<_Compare> _M_impl;
};
रेड-ब्लैक ट्री का रूट, सुदूर बाएँ तथा सुदूर दाएँ नोड प्राप्त करना
शुरुआत के चित्र पर लौटते हैं:
।
// 图中100 节点
_Base_ptr&
_M_root() _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_header._M_parent; }
// 图中most left标记
_Base_ptr&
_M_leftmost() _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_header._M_left; }
// 图中most right标记
_Base_ptr&
_M_rightmost() _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_header._M_right; }
_Link_type
// 图中begin()标记
_M_begin() _GLIBCXX_NOEXCEPT
{ return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }
// 图中end()标记
_Link_type
_M_end() _GLIBCXX_NOEXCEPT
{ return reinterpret_cast<_Link_type>(&this->_M_impl._M_header); }
5.रेड-ब्लैक ट्री इंसर्शन¶
5.1 रोटेशन प्रक्रिया¶
बाएँ रोटेशन में इस नोड के दाएँ नोड को उसका पिता नोड बना दिया जाता है, और यह नोड अभी-अभी वाले दाएँ नोड का बायाँ बच्चा बन जाता है।
सीधे सोर्स कोड के चित्र तथा कोड की तुलना देख लीजिए।
tree.cc सोर्स कोड में कार्यान्वयन फंक्शन्स local_Rb_tree_rotate_left तथा local_Rb_tree_rotate_right हैं।
नीचे हम सोर्स कोड को विश्लेषण करके अपेक्षाकृत सरलता से समझे जाने वाले कोड में प्रस्तुत करते हैं; विवरण टिप्पणियों में देखिए।
आप पाएँगे कि फंक्शन तथा वेरिएबल नाम सोर्स कोड से भिन्न हैं — क्योंकि नीचे दिया कोड तब स्वयं कार्यान्वित किया गया था, पर यह सोर्स कोड पढ़ने में बाधक नहीं है, इसलिए सीधे तुलना हेतु ले लिया गया है।
/**
* 当前节点的左旋转过程
* 将该节点的右节点设置为它的父节点,该节点将变成刚才右节点的左孩子
* @param _x
*/
// _x _y
// / \ 左旋转 / \
// T1 _y ---------> _x T3
// / \ / \
// T2 T3 T1 T2
void leftRotate(Node *_x) {
// step1 处理_x的右孩子
// 右节点变为_x节点的父亲节点,先保存一下右节点
Node *_y = _x->right;
// T2变为node的右节点
_x->right = _y->left;
if (NULL != _y->left)
_y->left->parent = _x;
// step2 处理_y与父亲节点关系
_y->parent = _x->parent; // 原来_x的父亲变为_y的父亲
// 说明原来_x为root节点,此时需要将_y设为新root节点
// 或者判断NULL == _y->parent
if (_x == root)
root = _y;
else if (_x == _x->parent->left) // 原_x的父节点的左孩子连接新节点_y
_x->parent->left = _y;
else // 原_x的父节点的右孩子连接新节点_y
_x->parent->right = _y;
// step3 处理_x与_y关系
_y->left = _x; // _y的左孩子为_x
_x->parent = _y; // _x的父亲是_y
}
इसी प्रकार, दाएँ रोटेशन:
// _x _y
// / \ 右旋转 / \
// _y T2 -------------> T0 _x
// / \ / \
// T0 T1 T1 T2
void rightRotate(Node *_x) {
// step1 处理_x的左孩子
// 左节点变为_x节点的父亲节点,先保存一下左节点
Node *_y = _x->left;
// T1变为_x的左孩子
_x->left = _y->right;
if (NULL != _y->right)
_y->right->parent = _x;
// step2 处理_y与父节点之间的关系
// 或者判断_x->parent==NULL
if (_x == root)
root = _y;
else if (_x == _x->parent->right)
_x->parent->right = _y;
else
_x->parent->left = _y;
// step3 处理_x与_y关系
_y->right = _x; // _y的右孩子为_x
_x->parent = _y; // _x的父亲是_y
}
case 1.1: पिता नोड लाल है और उसका अंकल नोड भी लाल है — तो पिता तथा अंकल को काला, तथा दादा को लाल कर दिया जाता है।

case 1.2 यदि अंकल नोड नहीं है या अंकल नोड काला है, तो नीचे दो भाग होते हैं:
स्थिति 1.2.1: x का अंकल नोड y काला है और x एक दायाँ बच्चा है

स्थिति 1.2.2: x का अंकल नोड y काला है और x एक बायाँ बच्चा है

सोर्स कोड में संगत:
_Rb_tree_node_base *const __y = __xpp->_M_right; // 得到叔叔节点
if (__y && __y->_M_color == _S_red) // case1: 叔叔节点存在,且为红色
{
/**
* 解决办法是:颜色翻转,父亲与叔叔的颜色都变为黑色,祖父节点变为红色,然后当前节点设为祖父,依次网上来判断是否破坏了红黑树性质
*/
__x->_M_parent->_M_color = _S_black; // 将其父节点改为黑色
__y->_M_color = _S_black; // 将其叔叔节点改为黑色
__xpp->_M_color = _S_red; // 将其祖父节点改为红色
__x = __xpp; // 修改_x,往上回溯
} else { // 无叔叔或者叔叔为黑色
if (__x == __x->_M_parent->_M_right) { // 当前节点为父亲节点的右孩子
__x = __x->_M_parent;
local_Rb_tree_rotate_left(__x, __root); // 以父节点进行左旋转
}
// 旋转之后,节点x变成其父节点的左孩子
__x->_M_parent->_M_color = _S_black; // 将其父亲节点改为黑色
__xpp->_M_color = _S_red; // 将其祖父节点改为红色
local_Rb_tree_rotate_right(__xpp, __root); // 以祖父节点右旋转
}
दूसरा उपरोक्त की सममित प्रक्रिया है:
case 2.1: पिता नोड लाल है और उसका अंकल नोड भी लाल है — तो पिता तथा अंकल को काला, तथा दादा को लाल कर दिया जाता है।

case 2.2 यदि अंकल नोड नहीं है या अंकल नोड काला है

स्थिति 2.2.1: x का अंकल नोड y काला है और x एक बायाँ बच्चा है

_Rb_tree_node_base *const __y = __xpp->_M_left; // 保存叔叔节点
if (__y && __y->_M_color == _S_red) { // 叔叔节点存在且为红色
__x->_M_parent->_M_color = _S_black; // 父亲节点改为黑色
__y->_M_color = _S_black; // 祖父节点改为红色
__xpp->_M_color = _S_red;
__x = __xpp;
} else { // 若无叔叔节点或者其叔叔节点为黑色
if (__x == __x->_M_parent->_M_left) { // 当前节点为父亲节点的左孩子
__x = __x->_M_parent;
local_Rb_tree_rotate_right(__x, __root); // 以父节点右旋转
}
__x->_M_parent->_M_color = _S_black; // 父节点置为黑色
__xpp->_M_color = _S_red; // 祖父节点置为红色
local_Rb_tree_rotate_left(__xpp, __root); // 左旋转
}
_Rb_tree_insert_and_rebalance का पूर्ण विश्लेषण:
void
_Rb_tree_insert_and_rebalance(const bool __insert_left,
_Rb_tree_node_base *__x,
_Rb_tree_node_base *__p,
_Rb_tree_node_base &__header) throw() {
_Rb_tree_node_base * &__root = __header._M_parent;
// Initialize fields in new node to insert.
__x->_M_parent = __p;
__x->_M_left = 0;
__x->_M_right = 0;
__x->_M_color = _S_red;
// 处理__header部分
// Insert.
// Make new node child of parent and maintain root, leftmost and
// rightmost nodes.
// N.B. First node is always inserted left.
if (__insert_left) {
__p->_M_left = __x; // also makes leftmost = __x when __p == &__header
if (__p == &__header) {
__header._M_parent = __x;
__header._M_right = __x;
} else if (__p == __header._M_left)
__header._M_left = __x; // maintain leftmost pointing to min node
} else {
__p->_M_right = __x;
if (__p == __header._M_right)
__header._M_right = __x; // maintain rightmost pointing to max node
}
// Rebalance.
while (__x != __root
&& __x->_M_parent->_M_color == _S_red) // 若新插入节点不是为RB-Tree的根节点,且其父节点color属性也是红色,即违反了性质4.
{
_Rb_tree_node_base *const __xpp = __x->_M_parent->_M_parent; // 祖父节点
if (__x->_M_parent == __xpp->_M_left) // 父亲是祖父节点的左孩子
{
_Rb_tree_node_base *const __y = __xpp->_M_right; // 得到叔叔节点
if (__y && __y->_M_color == _S_red) // case1: 叔叔节点存在,且为红色
{
/**
* 解决办法是:颜色翻转,父亲与叔叔的颜色都变为黑色,祖父节点变为红色,然后当前节点设为祖父,依次网上来判断是否破坏了红黑树性质
*/
__x->_M_parent->_M_color = _S_black; // 将其父节点改为黑色
__y->_M_color = _S_black; // 将其叔叔节点改为黑色
__xpp->_M_color = _S_red; // 将其祖父节点改为红色
__x = __xpp; // 修改_x,往上回溯
} else { // 无叔叔或者叔叔为黑色
if (__x == __x->_M_parent->_M_right) { // 当前节点为父亲节点的右孩子
__x = __x->_M_parent;
local_Rb_tree_rotate_left(__x, __root); // 以父节点进行左旋转
}
// 旋转之后,节点x变成其父节点的左孩子
__x->_M_parent->_M_color = _S_black; // 将其父亲节点改为黑色
__xpp->_M_color = _S_red; // 将其祖父节点改为红色
local_Rb_tree_rotate_right(__xpp, __root); // 以祖父节点右旋转
}
} else { // 父亲是祖父节点的右孩子
_Rb_tree_node_base *const __y = __xpp->_M_left; // 保存叔叔节点
if (__y && __y->_M_color == _S_red) { // 叔叔节点存在且为红色
__x->_M_parent->_M_color = _S_black; // 父亲节点改为黑色
__y->_M_color = _S_black; // 祖父节点改为红色
__xpp->_M_color = _S_red;
__x = __xpp;
} else { // 若无叔叔节点或者其叔叔节点为黑色
if (__x == __x->_M_parent->_M_left) { // 当前节点为父亲节点的左孩子
__x = __x->_M_parent;
local_Rb_tree_rotate_right(__x, __root); // 以父节点右旋转
}
__x->_M_parent->_M_color = _S_black; // 父节点置为黑色
__xpp->_M_color = _S_red; // 祖父节点置为红色
local_Rb_tree_rotate_left(__xpp, __root); // 左旋转
}
}
}
//若新插入节点为根节点,则违反性质2
//只需将其重新赋值为黑色即可
__root->_M_color = _S_black;
}
5.2 इंसर्शन सारांश¶
उपरोक्त इंसर्शन प्रक्रिया तथा सोर्स कोड विश्लेषण के आधार पर, हम निम्नलिखित तीन स्थितियाँ निकालते हैं: मान लीजिए P पिता नोड है, N अभी नया इंसर्ट हुआ वर्तमान नोड, U अंकल नोड, तथा G दादा नोड दर्शाता है।
case 1: U लाल है, तथा P, N भी लाल हैं — रंग बदलकर, तल से ऊपर की ओर पुनरावर्ती समायोजन किया जा सकता है; अगली बार N ही G बन जाता है, बस ऊपर जाकर निर्णय करना है। यदि संयोगवश रूट नोड लाल रंग हो जाए, तो एल्गोरिद्म के अंत में root को बलपूर्वक काला किया जा सकता है।

case 2: U काला है — विचार करें कि N, P का बायाँ बच्चा है या दायाँ।
case2.1 यदि दायाँ बच्चा है, तो पहले बाएँ रोटेशन करें, फिर अगली स्थिति में जाएँ।

case2.2 यह उपरोक्त स्थिति से बदलकर आया हो सकता है, पर हमेशा नहीं! रणनीति है: दाएँ रोटेशन, रंग बदलना।

उपरोक्त सोर्स कोड विश्लेषण से ज्ञात हुआ कि रेड-ब्लैक ट्री का इंसर्शन दर्पण-रूपांतरण है — दूसरी स्थिति ठीक इसके विपरीत होती है।
डिलीशन ऑपरेशन काफी जटिल है, इसीलिए यहाँ अभी उसका विश्लेषण नहीं किया गया — बाद में जोड़ा जाएगा...
6.उपयोग¶
इतना कुछ कहने के बाद — उपयोग कैसे करें?
हेडर फ़ाइल शामिल करें:
क्लास परिभाषा: फिर संगत फंक्शन्स को कॉल कर दें।संबंधित विषय¶
- STL array सोर्स कोड विश्लेषण | C++那些事
- STL string गहन विश्लेषण | C++那些事
- STL string इंटरव्यू प्रश्न विस्तृत व्याख्या | C++那些事
- STL deque सोर्स कोड विश्लेषण | C++那些事
- STL खाली बेस क्लास ऑप्टिमाइज़ेशन EBO | C++那些事
- STL स्वहस्त लिखी हैश तालिका कार्यान्वयन | C++那些事
- STL hashtable हैश तालिका | C++那些事
- STL इटरेटर सोर्स कोड विश्लेषण | C++那些事
- STL list सोर्स कोड विश्लेषण | C++那些事
- STL map तथा multimap | C++那些事