红黑树 RedBlackTree

 参考算法导论第三版第十二章: 二叉搜索树。实现代码:百度网盘,提取密码:n6yd。

 当普通的二叉搜索树高低较低时,对于树的操作效率较高,考虑这样一种情况:按照关键字从小到大(或从大到小)构建一颗搜索二叉树,这样的一颗树实际上就是一个线性链表,搜索性能很差,为O(n)。与理想的lg(n)有较大差距。当然这是一种最坏情况。而红黑树就是一颗加强版的二叉搜索树,它是一个近似平衡的(从根结点到叶结点的路径中,没有一条路径会比最短路径长出二倍),而当对树进行插入、删除操作后会破坏树的平衡性,要进行一系列操作维护这种平衡性,以达到最坏情况下时间复杂度仍然是lg(n)。


红黑树定义:


 红黑树中的结点中维护一个颜色域,表明当前结点是红色结点还是黑色结点。这也是红黑树名字的又来。树中每个结点包含五个属性:color、key-value、left、right和p。如果一个结点没有子结点或父结点,则相应的指针属性为NIL(视NIL为叶结点)。算法导论中对红黑树做如下定义:

 1.每个结点或是红色的,或是黑色的。
 2.根结点是黑色的。
 3.每个叶结点(NIL)是黑色的。
 4.如果一个结点是红色的,则它的两个子结点都是黑色的。
 5.对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。(黑高)

旋转:


 当对一颗红黑树执行插入、修改操作后,红黑树的性质就有可能被破坏。为了维护红黑树的性质,就必须要改变某些结点的颜色和指针(left、right、p)结构。指针结构能够通过【旋转】操作来完成,包括左旋和右旋。对结点x执行左旋,相当于用其右子结点y代替其位置,同时令结点y的左子结点β作为结点x的右子结点,结点x作为结点y的左子结点。右旋与左旋对称。

/**
 * 左旋操作。右旋与左旋对称,只要把right和left调换
 * @param p 对p结点进行左旋操作
 */
private void leftRotate(TreeNode<K, V> p) { // p对应x,r对应y
    if (p != null) {
        TreeNode<K, V> r = p.right;
        p.right = r.left;
        if (r.left != NIL)
            r.left.parent = p;
        r.parent = p.parent;
        if (p.parent == NIL)
            root = r;
        else if (p == p.parent.left)
            p.parent.left = r;
        else
            p.parent.right = r;
        r.left = p;
        p.parent = r;
    }
}

 插入:

/**
 * 与搜索二叉树插入情况基本类似,每个新插入节点为红色,插入后
 * 可能会导致红黑树特性被破坏,需要进行颜色转换和旋转操作修复
 * @param key 插入的关键字
 * @param value 对应的值
 * @param putIfAbsent 设置为true时,只有当前树中不存在关键字key时,执行插入
 */
@SuppressWarnings("unchecked")
private void insert(K key, V value, boolean putIfAbsent) {
    TreeNode<K, V> p = root, r = nil;
    int cmp;
    while (p != nil) {
        r = p;
        cmp = key.compareTo(p.key);
        if (cmp == 0) {
            if (putIfAbsent || p.value == null)
                p.value = value;
            return;
        } else if (cmp < 0)
            p = p.left;
        else
            p = p.right;
    }
    TreeNode<K, V> t = newTreeNode(key, value, r);
    if (r == nil)
        root = t;
    else if (key.compareTo(r.key) < 0)
        r.left = t;
    else
        r.right = t;
    size++;
    insertFixUp(t); // 插入节点后,进行修复操作
}


插入后修复:

private void insertFixUp(TreeNode<K, V> t) {
    while (t.parent.color == RED) {
        TreeNode<K, V> uncle;
        if (t.parent == t.parent.parent.left) { // 父结点为左子结点
            uncle = t.parent.parent.right;
            if (uncle.color == RED) { // 情况一,父结点和叔父结点均为红色
                // 将父父结点和叔父结点均换成黑色,然后修改祖父结点为红色
                t.parent.color = BLACK;
                uncle.color = BLACK;
                t.parent.parent.color = RED;
                // 此时祖父结点有可能不符合红黑树定义,对其进行修正
                t = t.parent.parent;
            } else if (t == t.parent.right) { // 情况二,叔父结点为黑色,结点t为右子结点
                t = t.parent; // 对父结点进行左旋操作,变成情况三
                leftRotate(t);
                t.parent.color = BLACK; // 情况三,将父结点修改成黑色,祖父结点修改成红色,然后右旋祖父结点
                t.parent.parent.color = RED;
                rightRotate(t.parent.parent);
            }
        } else { // 父结点为右子结点,与父结点为左子结点对称(left和right调换)
            uncle = t.parent.parent.left;
            if (uncle.color == RED) {
                t.parent.color = BLACK;
                uncle.color = BLACK;
                t.parent.parent.color = RED;
                t = t.parent.parent;
            } else if (t == t.parent.left) {
                t = t.parent;
                rightRotate(t);
                t.parent.color = BLACK;
                t.parent.parent.color = RED;
                leftRotate(t.parent.parent);
            }
        }
    }
    root.color = BLACK;
}

一个简单的示例:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值