本文着重介绍了红黑树的插入和删除操作,由于网上很多介绍红黑树的博文只介绍操作过程,而不解释为什么进行该操作,或者仅仅用“符合红黑树的五个性质”一带而过,令人费解。因此,本文在介绍各种插入和删除操作的同时,会详细解释各个操作的作用。希望本文能够帮助你快速理解红黑树的插入和删除过程。(本文图片来自维基百科,https://en.wikipedia.org/wiki/Red_black_tree)
1、定义
红黑树是一种高度平衡的二叉搜索树,包含5个性质:
(1) 节点不是红的,就是黑的。
(2) 根节点是黑的。
(3) 所有叶子节点都是黑的(叶子是NIL节点)。
(4) 每个红色节点的两个子节点都是黑的。(从根到叶子节点的所有路径上都不能有两个连续的红色节点)
(5) 从任一节点到其每个叶节点的所有路径都包含相同数量的黑色节点。
注意,红黑树是高度平衡,而不是绝对平衡。由性质4、5可知,两个子树的黑色节点数量相同,当一棵子树没有红色节点,而另一棵子树有尽可能多的红色节点时,两棵子树最大的高度差只有2倍。
2. 数据结构
红黑树的每个节点都包含了四个基本属性,分别是颜色(默认是黑色)、父节点、左子结点、右子节点。以下摘自红黑树在STL的结构定义:
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;
……
};
3. 插入操作
插入点应设为红色,不能是黑色。因为插入黑色点,将破坏性质5。但如果插入点的父节点也是红色,不也破坏了性质4?是的,但考虑到恢复性质4比恢复性质5更简单,因此插入红色节点。当性质被破坏时,通过重新着色和旋转进行恢复。为描述方便,插入节点为N(红色),父节点为P,祖父节点为G,叔叔节点为U。所有插入情况如下:
case 0.0:该树为空树,直接插入根节点,此时违反性质2,将节点改为黑色即可。
case 0.1 : 插入点N的父节点P为黑色,不违反任何性质(注意,根据二叉查找树的性质,N的两个子节点都为叶节点),结束。
当父节点P为红色时,违反性质4,分三种情况调整(此时N为红,P为红,有G且G一定为黑):
case 1:叔叔节点U为红,如图:
操作:P、U变为黑,G变为红。
解析:子树恢复了性质4,但是G的父节点可能为黑,也可能为红。如果G的父节点为黑,则结束;如果为红,就把G作为起始点,向上检索,根据case1、2、3继续处理。(如果处理到最后,根节点变为红色,则把根节点再改为黑色即可)
case 2:U为黑,N为P的左孩子,P为G的左孩子(或者N为P的右孩子,P为G的右孩子,即同向),如图:
操作:P和G交换颜色,并右旋。(对于N、P都为右情况,此处是左旋)
解析:P变为黑色后,左子树恢复了性质4;接着,右旋使P处于G最初的位置,而G变为红色后不影响右子树的黑色节点个数。结束。
case 3:U为黑,N为P的右孩子,P为G的左孩子(或者N为P的左孩子,P为G的右孩子,即反向),如图:
操作:左旋。(对于N为左、P为右的情况,此处是右旋)
解析:左旋后,P、N交换了上下的位置,且都为左孩子,此时变成了case 2,继续进行case 2的操作即可。
4. 删除操作
二叉查找树的删除有个技巧:先查找到要删除的节点X,然后找到它左子树的最大元素节点N或右子树的最小元素节点N(N最多只有一个非叶子节点),用N的值替换X的值;而N的位置则用它的子节点替换即可。为描述方便,N为替换节点,P为N的父节点,S为N的兄弟节点,SL、SR分别为S的左右子节点。具体删除情况分类如下:
case 0.0:N为红色节点,那么N的两个子节点一定为NIL(黑色),直接删除N即可,不违反任何性质。
case 0.1:N为黑色节点,且有一个非叶子结点M,那么M一定为红色,且两个子节点一定为NIL,此时用M替换N,将M的颜色改为黑即可。
当N为黑,且两个子节点都为NIL时,删除N使通过N的路径少了一个黑色节点,破换了性质5,分五种情况讨论:
case 1:兄弟节点S为红(此时,P、SL、SR一定为黑),如图:
操作:P和S交换颜色,并左旋。
解析:该操作只是使N的父节点变为红色,但是通过N的路径仍然少了一个黑点,因此需要根据case3、4、5继续调整。
case 2:S、SL、SR、P都为黑,如图:
操作:S变为红。
解析:S变为红之后,通过S的路径少了一个黑节点,使P左右子树的黑节点个数相等。但是通过P的路径比不通过P的路径少了一个黑色节点,因此要以P为起始点,向上递归处理。
case 3:S、SL、SR都为黑,但P为红,如图:
操作:P和S交换颜色。
解析:此时通过N的路径多了一个黑色节点(P),同时,通过S的路径的黑色节点个数不变,不违反所有性质,结束。
case 4:N为P的左孩子,S为黑,SR为红,P和SL任意颜色,如图:
操作:P和S交换颜色,左旋,并把SR变为黑。
解析:此时通过N的路径多了一个黑色节点(P);而S和P交换颜色并左旋之后,使通过S的路径少了一个黑色节点,但由于SR由红变成了黑色,因此通过S的路径的黑色节点个数不变,结束。
case 5:N为P的左孩子,S、SR都为黑,SL为红,P任意颜色,如图:
操作:SL和S交换颜色,并右旋。
解析:由于这种情况下无法通过case 4的操作来调整好,因为SR本身就是黑色,不能由红变黑,增加黑色节点个数。因此,通过SL和S交换颜色并右旋,使SL处于S的最初位置,并且SL的右孩子为红色,变成了case 4。