<红黑树并无咱们想象的那么难> 上、下两篇已经完成, 但愿能帮助到你们.html
红黑树并无想象的那么难, 初学者以为晦涩难读多是由于状况太多. 红黑树的状况能够经过归结, 经过合并来获得更少的状况, 如此能够加深对红黑树的理解. 网络上的大部分成黑树的讲解由于没有「合并」. 红黑树的五个性质:node
性质1. 节点是红色或黑色。python
性质2. 根是黑色。算法
性质3. 全部叶子都是黑色(叶子是NIL节点)。bash
性质4. 每一个红色节点的两个子节点都是黑色。(从每一个叶子到根的全部路径上不能有两个连续的红色节点)网络
性质5. 从任一节点到其每一个叶子的全部简单路径 都包含相同数目的黑色节点。数据结构
摘自 sgi stl 红黑树数据结构定义:svg
typedef bool _Rb_tree_Color_type; const _Rb_tree_Color_type _S_rb_tree_red = false; const _Rb_tree_Color_type _S_rb_tree_black = true; struct _Rb_tree_node_base { typedef _Rb_tree_Color_type _Color_type; typedef _Rb_tree_node_base* _Base_ptr; _Color_type _M_color; _Base_ptr _M_parent; _Base_ptr _M_left; _Base_ptr _M_right; static _Base_ptr _S_minimum(_Base_ptr __x) { while (__x->_M_left != 0) __x = __x->_M_left; return __x; } static _Base_ptr _S_maximum(_Base_ptr __x) { while (__x->_M_right != 0) __x = __x->_M_right; return __x; } }; template <class _Value> struct _Rb_tree_node : public _Rb_tree_node_base { typedef _Rb_tree_node<_Value>* _Link_type; _Value _M_value_field; };
在展开红黑树以前, 首先来看看普通二叉搜索树的插入和删除. 插入很容易理解, 比当前值大就往右走, 比当前值小就往左走. 详细展开的是删除操做.lua
二叉树的删除操做有一个技巧, 即在查找到须要删除的节点 X; 接着咱们找到要么在它的左子树中的最大元素节点 M、要么在它的右子树中的最小元素节点 M, 并交换(M,X). 此时, M 节点必然至多只有一个孩子; 最后一个步骤就是用 M 的子节点代替 M 节点就完成了. 因此, 全部的删除操做最后都会归结为删除一个至多只有一个孩子的节点, 而咱们删除这个节点后, 用它的孩子替换就行了. 将会看到 sgi stl map 就是这样的策略.spa
在红黑树删除操做讲解中, 咱们假设代替 M 的节点是 N(下面的讲述再也不出现 M).
插入新节点老是红色节点, 由于不会破坏性质 5, 尽量维持全部性质.
假设, 新插入的节点为 N, N 节点的父节点为 P, P 的兄弟(N 的叔父)节点为 U, P 的父亲(N 的爷爷)节点为 G. 因此有以下的印象图:
插入节点的关键是:
插入算法详解以下, 走一遍红黑树维持其性质的过程:
第 0.0 种状况, N 为根节点, 直接 N->黑. over
第 0.1 种状况, N 的父节点为黑色, 这不违反红黑树的五种性质. over
第 1 种状况, N,P,U 都红(G 确定黑). 策略: G->红, N,P->黑. 此时, G 红, 若是 G 的父亲也是红, 性质又被破坏了, HACK: 能够将 GPUN 当作一个新的红色 N 节点, 如此递归调整下去; 特俗的, 若是碰巧将根节点染成了红色, 能够在算法的最后强制 root->黑.
第 2 种状况, P 为红, N 为 P 右孩子, N 为红, U 为黑或缺乏. 策略: 旋转变换, 从而进入下一种状况:
第 3 种状况, 可能由第二种变化而来, 但不是必定: P 为红, N 为 P 左孩子, N 为红. 策略: 旋转, 交换 P,G 颜色, 调整后, 由于 P 为黑色, 因此不怕 P 的父节点是红色的状况. over
红黑树的插入就为上面的三种状况. 你能够作镜像变换从而获得其余的状况.
假设 N 节点见上面普通二叉树删除中的定义, P 为 N 父节点, S 为 N 的兄弟节点, SL,SR 分别是 S 的左右子节点. 有以下印象图:
删除节点的关键是:
删除算法详解以下, 走一遍红黑树维持其性质的过程:
第 0.0 状况, N 为根节点. over
第 0.1 状况, 删除的节点为红. over
第 0.2 状况, 删除节点为黑, N 为红. 策略: N->黑, 从新平衡. over
第 1 状况, N,P,S,SR,SL 都黑. 策略: S->红. 经过 PN,PS 的黑色节点数量相同了, 但会比其余路径多一个, 解决的方法是在 P 上从状况 0 开始继续调整. 为何要这样呢? HANKS: 由于既然 PN,PS 路径上的黑节点数量相同并且比其余路径会少一个黑节点, 那何不将其总体当作了一个 N 节点! 这是递归原理.
第 2 状况, S 红, 根据红黑树性质 P,SL,SR 必定黑. 策略: 旋转, 交换 P,S 颜色. 处理后关注的范围缩小, 下面的状况对应下面的框图, 算法从框图从新开始, 进入下一个状况:
第 2.1 状况, S,SL,SR 都黑. 策略: P->黑. S->红, 由于经过 N 的路径多了一个黑节点, 经过 S 的黑节点个数不变, 因此维持了性质 5. over. 将看到, sgi stl map 源代码中将第 2.1 和第 1 状况合并成一种状况, 下节展开.
第 2.2.1 状况, S,SR 黑, SL 红. 策略: 旋转, 变换 SL,S 颜色. 从而又进入下一种状况:
第 2.2.2 状况, S 黑, SR 红. 策略: 旋转, 交换 S,P 颜色, SR->黑色, 从新得到平衡.
上面状况标号 X.X.X 并非说这些关系是嵌套的, 只是这样展开容易理解. 此时, 解释三个地方:
红黑树删除从新调整伪代码以下:
// 第 0.0 状况, N 为根节点. over if N.parent == NULL: return; // 第 0.1 状况, 删除的节点为红. over if color == RED: return; // 第 0.2 状况, 删除节点为黑, N 为红, 简单变换: N->黑, 从新平衡. over if color == BLACK && N.color == RED: N.color = BLACK; // 第 1 种状况, N,P,S,SR,SL 都黑. 策略: S->红. 经过 N,S 的黑色节点数量相同了, 但会比其余路径多一个, 解决的方法是在 P 上从状况 0 开始继续调整. if N,P,S,SR,SL.color == BLACK: S.color = RED; // 调整节点关系 N = P N.parent = P.parent S = P.paernt.another_child SL = S.left_child SR = S.right_child continue; // 第 2 状况, S 红, 根据红黑树性质 P,SR,SL 必定黑. 旋转, 交换 P,S 颜色. 此时关注的范围缩小, 下面的状况对应下面的框图, 算法从框图从新开始. if S.color == RED: rotate(P); swap(P.color,S.color); // 调整节点关系 S = P.another_child SL = S.left_child SR = S.right_child // 第 2.1 状况, S,SL,SR 都黑. 策略: P->黑. S->红, 由于经过 N 的路径多了一个黑节点, 经过 S 的黑节点个数不变, 因此维持了性质 5. over. 将看到, sgi stl map 源代码中将第 2.1 和第 1 状况合并成一种状况, 下节展开. if S,SL,SR.color == BLACK: P.color = BLACK; S.color = RED; return // 第 2.2.1 状况, S,SR 黑, SL 红. 策略: 旋转, 变换 SL,S 颜色. 从而又进入下一种状况: if S,SR.color == BLACK && SL.color == RED: rotate(P); swap(S.color,SL.color); // 调整节点关系 S = SL SL = S.left_child SR = S.right_child // 第 2.2.2 状况, S 黑, SR 红. 策略: 旋转, 交换 S,P 颜色. if S.color == BLACK && SR.color == RED: rotate(P); swap(P.color,S.color); return;
因此, 插入的状况只有三种, 删除的状况只有两种. 上面的分析, 作镜像处理, 就能获得插入删除的所有算法, 脑补吧. 从上面的分析来看, 红黑树具备如下特性: 插入删除操做都是 0(lnN), 且最多旋转三次.
下节中会重点展开 sgi stl map 的源代码.
参考文档: wikipedia
捣乱 2013-9-25