理解“红黑树”

(本文主要参考与网络上一些文章加上自己的理解,代码来自JULY http://blog.csdn.net/v_JULY_v/article/details/6169600 )

在真正理解红黑树之前,我觉得它是一座难以翻越的大山;当我有点理解了它,如释重负。
看红黑树之前,我看了AVL树,觉得还是比较好理解,对于红黑树,我曾经有一些疑问:
1.为什么要用红黑来标记?
2.红黑结点如何做到平衡?
3.相对于AVL树,红黑树的优势、意义是什么呢?
在逐步学习的过程中,我大概理解了上述疑问。

首先,红黑树必须满足下面几条性质:
1. 每个结点要么是红的要么是黑的。  
2. 根结点是黑的。   
3. 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
4. 如果一个结点是红的,那么它的两个儿子都是黑的(没有连续的2个红色结点)  。
5. 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。 
对于上述1,2,3属于基本规则,遵守就行了;
4,5相辅相成,是保证平衡的关键,确保了左右子树高度相差最大为"2倍"(多的那1倍都是红色结点)。
相对于AVL树,AVL树左右子树高度相差最大为"1".

关于插入 
每次插入的都是红色结点。(当父结点为黑色时,不做任何操作,减少了旋转与涂色操作)
插入过程:
1.插入(必须满足BST)
2.判断是否满足红黑树性质
3.不满足,旋转,改变结点颜色
  (或先改变结点颜色,再旋转,详见代码)
4.再判断是否满足性质
5.再旋转、改变结点颜色

旋转的过程与AVL是一样的,参考前一篇文章《AVL树》

关于插入改变结点颜色可以总结为下面几种情况:
当插入一个(子)结点时(插入结点为红色),
1.插入的是根结点,直接涂黑色;
2.父结点为黑色,不做任何改变;
3.父结点为红色,叔结点为红:
  父结点涂黑色: uncle->color = BLACK;
  叔结点涂黑色: parent->color = BLACK;
  祖结点涂红色: gparent->color = RED; 
  (如果祖结点为根结点,再改为黑色:root->color = BLACK;)
4.父结点为红色,叔结点为黑(或NULL,NIL) 
  在(变换)为 根-左-左 或 根-右-右 情况下:
  父结点(未来的根节点)涂黑色: parent->color = BLACK;
  祖结点(未来的叔节点)涂红色:gparent->color = RED;
  再右旋或左旋

关于删除
删除相对很繁琐,我的思路很简单:左侧少了一个黑结点,右侧必须“想方设法”消除一个。
删除先按照BST的方法删除结点,大概可归纳为:
1.删除结点为红色结点,直接删除,不会影响红黑树性质
2.删除结点为黑色结点(分三种情况):
  1.没有子结点
    以删除结点的叶子(NULL, 黑色)作为补充结点,进入: static rb_node_t* rb_erase_rebalance(rb_node_t *node, rb_node_t *parent, rb_node_t *root)
    此时参数中: node 为删除结点的叶子(NULL, 黑色)
            parent为删除结点的父结点
            兄弟结点other = parent->right;
 
  2.有一个子结点
    以删除结点的子结点作为补充结点,进入: static rb_node_t* rb_erase_rebalance(rb_node_t *node, rb_node_t *parent, rb_node_t *root)
此时的实参为:
    rb_erase_rebalance(child, parent, root);
对应的形参:node是曾经被删除结点的子结点,现在的“基准”结点,取代之前的“删除”结点
    parent是曾经被删除结点的父结点,也是现在node的父结点
 
  3.有2个子结点
    将删除结点用其用右子树最小结点值取代(或用左子树最大结点值取代),
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值