红黑树

简单介绍

  • avl树删除操作最坏情况需要O(logn)旋转;为了解决这一问题,需要将平衡的定义的更加宽泛些,这就是红黑树了(删除和新增只需要O(logn)时间)
  • 适度平衡
    • 没有平衡因子的概念,但是要求任一节点的左右子树高度不得相差2倍
  • 红黑条件
    1. 树根均是黑色
    2. 外部节点均是黑色
    3. 其余节点如果是红色,则其孩子节点必需是黑色
    4. 从任一外部节点到根节点的沿途,黑节点数目相等
  • 红黑条件解析
    1. 条件1,2: 红节点都是内部节点且父节点,左右孩子必然存在
    2. 条件3: 红节点的父节点必然是黑节点,黑红树的任一通路不含相邻的红节点, 从根节点通往任一节点的沿途,黑节点数目总不少于红节点数目
    3. 条件4: 从任一节点通往其任一后代的外部节点的沿途,黑节点的总数总是相等的。另外所有外部节点的黑深度相等

基本概念

  • 黑深度
    • 从节点到任一节点沿途经过的黑节点的数目-1(根节点) 就是黑深度
    • 根节点黑深度是0
  • 黑高度
    • 从任一节点通往其任一后代外部节点的沿途经过的黑节点数目-1(外部节点)
    • 外部节点黑高度是0

删除重平衡原理

代码实现

// 引入二叉树
#include "BstTree.h"

template<typename T>
class RedBlack : public BST<T> {
protected:
    // 双红修正
    void solveDoubleRed(BinNodePosi(T)x);

    // 双黑修正
    void solveDoubleBlack(BinNodePosi(T)x);

    // 更新高度
    int updateHeight(BinNodePosi(T)x);

public:
    BinNodePosi(T)insert(const T &e);

    bool remove(const T &e);
};

// 外部节点也当作黑色
#define IsBlack(p) (!(p) || (p)->color == RB_RED)

// 是否是红色
#define IsRed(p) (!IsBlack(p))

// RedBlack高度更新的条件
#define BlackHeightUpdated(x) (\
    (stature(x.lc) == stature(x.rc)) && \
    (x.height == (IsRed(&x) ? stature(x.lc) : stature(x.lc) + 1)) \
)

// 更新节点高度
template<typename T>
int RedBlack<T>::updateHeight(BinNodePosi(T)x) {
    // 双黑的情况下,
    x->height = max(stature(x->lc), stature(x->rc));

    // 黑节点比子节点高1
    return IsBlack(x) ? x->height++ : x->height;
};

// 节点插入
template<typename T>
BinNodePosi(T)::insert(const T &e) {
    // 如果存在 则不进行后续操作
    BinNodePosi(T)&x = search(e);

    if (x) {
        return x;
    }

    // 创建以_hot为父节点的红节点, 此节点黑高度-1
    x = new BinNode(e, _hot, NULL, NULL, -1);
    _size++;

    // 双击红修正

    return x ? x : _hot->parent;
};

/**
 * 双红修正算法:解决节点x与父亲都是红色的问题,分为下面的两类
 * RR-1. 叔父节点是黑色 2次颜色翻转,2次黑高度更新, 1--2次旋转
 * RR-2. 叔父节点是黑色 3次颜色翻转,3次黑高度更新,0次旋转 需要递归
 *
 * */
template<typename T>
void RedBlack<T>::solveDoubleRed(BinNodePosi(T)x) {
    // 递归基(根强制染成黑色, 整树高度+1)
    if (IsRoot(*x)) {
        x->color = RB_BLACK;
        x->height++;
        return;
    }

    // 如果p为黑 则递归终止
    BinNodePosi(T)p = x->parent;
    if (IsBlack(p)) {
        return;
    }

    // p为红色节点,则g必为黑色节点
    BinNodePosi(T)g = p->parent;

    // 叔父节点
    BinNodePosi(T)u = uncle(x);

    // RR-1
    if (IsBlack(u)) {
        // 如果x p同侧
        if (IsLChild(*p) == IsLChild(*x)) {
            p->color = RB_BLACK;
        } else {
            v->color = RB_BLACK;
        }
        // g必定转成红色
        g->color = RB_RED;

        // 曾祖
        BinNodePosi(T)gg = g->parent;

        // 翻转之后的根
        BinNodePosi(T)r = FromParentTo(*g) = rotateAt(x);
        r->parent = gg;

        // 此时黑高度恢复
    } else {
        p->color = RB_BLACK;
        p->height++;
        u->color = RB_BLACK;
        u->height++;

        // 如果g不是根节点,则染成红色
        if (!IsRoot(*g)) {
            g->color = RB_RED;
        }

        solveDoubleRed(g);
    };

}

// 删除算法
template<typename T>
bool RedBlack<T>::remove(const T &e) {
    // 节点不存在 不需要后续操作
    BinNodePosi(T)&x = search(e);

    if (!x) {
        return false;
    }

    // 删除节点
    BinNodePosi(T)r = removeAt(x, _hot);

    // 更新节点数量
    --size;

    // 空树没有必要纠结双黑的问题
    if (!_size) {
        return true;
    }

    // 如果删除的根节点, 将树根染黑,更新书高
    if (!_hot) {
        _root->color = RB_BLACK;
        updateHeight(_root);
        return true;
    }

    /**
     * 祖先的高度依然平衡 则不需要调整
     * x红 r红  Y
     * x红 r黑  Y
     * x黑 r黑  N
     * x黑 r红  N
     **/
    if (BlackHeightUpdated(*_hot)) {
        return true;
    }

    // x黑 r红
    if (IsRed(r)) {
        r->color = RB_BLACK;
        r->height++;
        return true;
    }

    // x黑 r黑
    // 双黑调整
    solveDoubleBlack(r);

    return true;
}

/** 双黑复原
 *  BB -1  黑S有红子t
 *      1.参考B-树下溢算法 借父节点合并到本身第0个关键码,父节点借S, 调整颜色(新父节s继承原父节点的p的颜色, t改成黑色,原父节点改成黑色) 此时达到平衡
 *      2.对于红黑树  t s p 以t为原点进行3+4旋转, 并修改颜色(新子根颜色继承原子根颜色,两个孩子修改成黑色),更新各自的高度
 *  BB-2-R 黑S无红子,父节点p为红色
 *      1. 参考B-树下溢算法 左s右x(空)孩子以父节点p为粘合剂合并成一个节点 , 调整颜色(p黑色,s红色)  (p所属的大节点中必然还有黑色关键码,所以此时不会产品下溢的传播 此时达到平衡)
 *      2. 对于红黑树 s,p颜色交换
 *  BB-2-B 黑S无红子 父节点p为黑色
 *      1. 参考B-树下溢算法  左s右x(空)孩子以父节点p为粘合剂合并成一个节点,  s调整成红色;  因原p时黑色即,s和x都是黑色,所以原p所属的大节点必然只有p一个关键码;此时p下溢之后导致向上传播
 *      2. 对红黑树 s颜色转成红色,递归解决下溢传播的问题solveDoubleBlack(p)
 *  BB-3 红S (因为S是红,所以此时P肯定是黑)
 *      1. 参考B-树 p和s交换一下颜色,此时没有解决r下溢的问题,但是solveDoubleBlack(r)已经转换成BB-1 BB-2-R的情况
 *      2. 对于红黑树  以和s同侧的孩子节点t为起点做了一次3+4的旋转并交换s和p的颜色
 *
 */
template<typename T>
void RedBlack<T>::solveDoubleBlack(BinNodePosi(T)r) {
    // 获取p级节点
    BinNodePosi(T)p = r ? r->parent : _hot; // r为NULL时_hot的值未removeAt中没有修改
    if (!p) {
        return;
    }

    // r的兄弟
    BinNodePosi(T)s = sibling(r);

    if (IsBlack(s)) {
        // s的红孩子 左孩子优先
        BinNodePosi(T)t = NULL;
        if (IsRed(s->rc)) {
            t = s->rc;
        }
        if (IsRed(s->lc)) {
            t = s->lc;
        }

        // BB - 1
        if (t) {
            RBColor old_color = p->color;
            // 3+4旋转
            BinNodePosi(T)b = FromParentTo(*p) = removeAt(t);
            // 更新
            if (HasRChild(b)) {
                b->rc->color = RB_BLACK;
                updateHeight(b->rc);
            }

            if (HasLChild(b)) {
                b->lc->color = RB_BLACK;
                updateHeight(b->lc);
            }

            b->color = old_color;
            updateHeight(b);

        } else {
            s->color = RB_RED;
            s->height--;
            if (IsRed(p)) {
                // BB-2-R
                // p 黑高度保持不变 (p红变黑, 理论上应该+1 但是删除了黑x, 黑s又变成了红s 所以p高度维持不变)
                p->color = RB_BLACK;
            } else {
                // BB-2-B
                // 删除了黑x, 黑s又变成了红s 所以p高度-1
                p->height --;
                solveDoubleRed(p);
            }
        }

    } else {
        // BB-3 红s 黑p (黑x 黑r)
        s->color = RB_BLACK;
        p->color = RB_RED;

        // 获取s同侧的孩子t
        BinNodePosi(T) t = IsLChild(*s) ? s->lc : s->rc;

        _hot = p;
        FromParentTo(*p) = rotateAt(t);

        // 继续修正r,转换成BB-1 BB-2-R的情况
        solveDoubleRed(r);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值