数据结构-手撕红黑树

手撕红黑树

1. 红黑树的性值

1.1 平衡条件

在这里插入图片描述

  1. 每个节点非黑即红
  2. 根节点是黑色
  3. 叶节点(NIL)是黑色
  4. 如果一个节点是红色,则它的两个子节点都是黑色
  5. 从任意节点出发到所有叶节点路径上,黑色节点数量相同

1.2最长路径和最短路径长度关系?

根据平衡条件第4、5点,最短路径都是黑色;最长路径红黑相间;最长是最短的两倍;

1.3平衡调整的终极法门

插入调整站在祖父节点看

删除调整站在父节点看

插入和删除的情况处理一共五种

1.4新插入的节点的颜色

红色,因为插入黑色会引发失衡;插入红色不一定会失衡。

2. 红黑树的插入

2.1 调整原则

为了不影响整颗树的平衡, 调整前路径上的黑节点数量等于调整后路径上黑色节点数量

2.2 调整目的

消除双红

2.3 调整方法

情况一:uncle为红色

站在15节点向下看,发现右子树为红色,并且右子树有一个红色的子节点 ==》 失衡
在这里插入图片描述
调整方法:

  • 15节点 ==》 变红

  • 1节点 ==》 变黑

  • 20节点 ==》 变黑

情况二:uncle为黑色

站在20节点向下看,左侧节点为红色,并且左侧节点的孩子有红色节点 ==》失衡
在这里插入图片描述
类似于AVL树的LL失衡调整 ==> 右旋
在这里插入图片描述
调整后的变色方案可以选择红色上浮或下沉

红色上浮:
在这里插入图片描述
红色下沉:

在这里插入图片描述

3. 红黑树的删除

3.1 删除场景

  • 场景1: 删除度为0的红色节点: 直接删除即可,不会引发失衡
    在这里插入图片描述

  • 场景2:删除度为1的红色节点:不存在这样的节点

若一个红色节点下面有子节点,一定同时有两个黑色子节点
原因:红黑树的第4条和第5条性值
性值4: 如果一个节点是红色,则它的两个子节点都是黑色
性值5: 从任一节点出发到所有叶节点路径上,黑色节点数量相同

  • 场景3(需要调整): 删除度为0的黑色节点
    在这里插入图片描述

删除X后,因X度为0,其下只有NIL节点;
红色根节点左子树一共两个黑色节点(包含NIL节点);所以该红色根节点右子树也需要有两个黑色节点 ==>
即,将该度为0 的黑色节点的颜色加到NIL节点上,至此,这个NIL节点变为双重黑
删除调整的目的就是为了消除双重黑节点。

  • 场景4: 删除度为1的黑色节点
    度为1的黑色节点的唯一子孩子一定是红色的,如下图所示:
    在这里插入图片描述
    删除X,将唯一子节点挂到父节点,将当前节点X的黑色加到唯一子孩子上。

3.2 调整场景

场景3中,删除度为0的黑色节点会引发红黑树的失衡,消除的双重黑。

  • 情况一 (双重黑的兄弟节点为黑色)
    双重黑X的兄弟为黑,且兄弟节点的子节点均为黑色节点:
    在这里插入图片描述

调整方法:

  • 双重黑 及 其兄弟节点 --> 去掉一重黑

  • 双重黑的父节点 --> 加一重黑

  • 情况二 RR失衡:双重黑的兄弟节点为黑色, 右子树的右子树为红色:
    双重黑为左子树,双重黑的兄弟节点的右子树为确定性红色(左子树颜色不确定)
    在这里插入图片描述
    若38为红色,则调整前左子树一共有2个黑,则调整后,仍需要为两个黑: 将51改为黑,38 & 72改为红;
    若38为黑色,则调整前左子树一共3个黑,调整后,仍需要为3个黑,将72改为黑。
    总结RR失衡调整策略:大左旋 => 2个子节点改为黑色,一个新根改为原根的颜色。

  • 情况三RL失衡:双重黑的兄弟节点为黑色,右子树的左子树为红色,右子树为黑色
    双重黑为左子树,双重黑的兄弟节点的左子树为红色,右子树为确定性黑色(一旦右子树为红,则为RR失衡)
    在这里插入图片描述
    调整方法:
    step1: 小右旋 => 将原根节点的颜色和新根节点的颜色对调(原根72改为红色,新根51改为黑色),变为情况二,图示 =》
    在这里插入图片描述

step2: 大左旋,即,调整为 =》
在这里插入图片描述

将新根(51)变为原根(38)的颜色
将新根(51)左右子树变为黑色
双重黑变为黑色

  • 情况四: (双重黑的兄弟节点为红色):
    step1: 根节点改为红色

step2: 情况(1)若 => 双重黑为右子树,根节点的左子树为红色:将该根节点右旋
​ 情况(2)若 => 双重黑为左子树,根节点的右子树为红色:将该根节点左旋
step3: 新根(原红色的兄弟节点)改为黑色
step4: 情况(1)
=> 递归到右子树
​ 情况(2)
=> 递归到左子树

下面图演示了 ==> 若双重黑为左子树,根节点的右子树为红色的情况
在这里插入图片描述

左旋 =》新根节点改为黑色,原根节点(双重黑的父节点)改为红色
在这里插入图片描述
至此,双重黑X节点的兄弟节点变为黑色,应递归到当前根节点的左子树中继续调整,该调整已经转换为上面的双重黑的兄弟节点为黑色的情况处理

代码:

public class RbTree {
    static Node NIL = new Node(0, 1, null, null);

    static class Node {
        int key;
        int color;//0:red; 1:black; 2:double black
        Node left;
        Node right;

        Node(int key, int color, Node left, Node right) {
            this.key = key;
            this.color = color;
            this.left = left;
            this.right = right;
        }

        Node(int key, int color) {
            this.key = key;
            this.color = color;
            this.left = NIL;
            this.right = NIL;
        }
    }

    Node getNewNode(int key) {
        return new Node(key, 0);
    }

    private Node _insert(Node root, int key) {
        if (root == NIL) return getNewNode(key);
        if (root.key == key) return root;
        if (key < root.key) {
            root.left = _insert(root.left, key);
        } else {
            root.right = _insert(root.right, key);
        }
        return insert_maintain(root);
    }

    private boolean has_red_child(Node root) {
        return root.left.color == 0 || root.right.color == 0;
    }

    private Node insert_maintain(Node root) {
        int flag = 0;
        if (root.left.color == 0 && has_red_child(root.left)) {
            flag = 1;
        }
        if (root.right.color == 0 && has_red_child(root.right)) {
            flag = 2;
        }
        if (flag == 0) {
            return root;
        }
        if (root.left.color == 0 && root.right.color == 0) {
            root.color = 0;
            root.left.color = 1;
            root.right.color = 1;
            return root;
        }
        if (flag == 1) {
            if (root.left.right.color == 0) {
                root.left = left_rotate(root.left);
            }
            root = right_rotate(root);
        } else {
            if (root.right.left.color == 0) {
                root.right = right_rotate(root.right);
            }
            root = left_rotate(root);
        }
        root.color = 0;
        root.left.color = 1;
        root.right.color = 1;
        return root;
    }

    private Node right_rotate(Node root) {
        Node temp = root.left;
        root.left = temp.right;
        temp.right = root;
        return temp;
    }

    private Node left_rotate(Node root) {
        Node temp = root.right;
        root.right = temp.left;
        temp.left = root;
        return temp;
    }

    Node insert(Node root, int key) {
        root = _insert(root, key);
        root.color = 1;
        return root;
    }

    Node _erase(Node root, int key) {
        if (root == null) return root;
        if (key < root.key) {
            root.left = _erase(root.left, key);
        } else if (key > root.key) {
            root.right = _erase(root.right, key);
        } else {
            if (root.left == NIL || root.right == NIL) {
                Node temp = root.left == NIL ? root.right : root.left;
                temp.color += root.color;
                return temp;
            } else {
                Node temp = predecessor(root);
                root.key = temp.key;
                root.left = _erase(root.left, temp.key);
            }
        }
        return erase_maintain(root);
    }

    private Node erase_maintain(Node root) {
        if (root.right.color != 2 && root.left.color != 2) return root;
        //情况四: 双重黑的兄弟节点为红色
        if (has_red_child(root)) {
            int flag = 0;
            root.color = 0;
            if (root.right.color == 0) {
                root = left_rotate(root);
                flag = 1;
            } else {
                root = right_rotate(root);
                flag = 2;
            }
            root.color = 1;
            if (flag == 1) {
                root.left = erase_maintain(root.left);
            } else {
                root.right = erase_maintain(root.right);
            }
            return root;
        }
        //情况一:双重黑的兄弟节点为黑色,且该兄弟节点下无红色节点
        if (root.left.color == 1 && !has_red_child(root.right) || root.right.color == 1 && !has_red_child(root.left)) {
            root.left.color -= 1;
            root.right.color -= 1;
            root.color += 1;
            return root;
        }
        //情况二 & 三:双重黑的兄弟节点为黑色,且该兄弟节点下存在红色节点
        if (root.left.color == 1) {//双重黑为右子树
            root.right.color = 1;//将双重黑节点变为黑色
            //若左子树的左子树为红色 ==> LL失衡
            //否则(若左子树的右子树为黑色,即,红色节点位于左子树的左子树) ==> LR失衡
            if (root.left.left.color != 0) {
                root.left.color = 0;//将左子树原根变红
                root.left = left_rotate(root.left);//将左子树左旋
                root.left.color = 1;//将左子树新根变黑
            }
            root.left.color = root.color;//将左子树小右旋后,将左子树的新根变为原根颜色
            root = right_rotate(root);//将根大右旋
        } else {//双重黑为左子树
            root.left.color = 1;//将双重黑节点变为黑色
            //若右子树的右子树为红色 ==> RR失衡
            //否则(右子树的右子树为黑色,即,红色节点为右子树的左子树) ==> RL失衡
            if (root.right.right.color != 0) {
                root.right.color = 0;//将右子树原根变红
                root.right = right_rotate(root.right);
                root.right.color = 1;//将右子树新根变黑
            }
            root.right.color = root.color;//将右子树小左旋后,将右子树中新的根节点的颜色变为原根节点的颜色
            root = left_rotate(root);//将根大左旋
        }
        //将新根节点的左右子树变为黑色
        root.right.color = 1;
        root.left.color = 1;
        return root;
    }

    private Node predecessor(Node root) {
        Node temp = root.left;
        while (temp.right != NIL) {
            temp = temp.right;
        }
        return temp;
    }

    Node erase(Node root, int key) {
        root = _erase(root, key);
        root.color = 1;
        return root;
    }

    static void outPut(Node root) {
        if (root == NIL) return;
        System.out.println(root.color + ", " + root.key + ", " + root.left.key + ", " + root.right.key);
        outPut(root.left);
        outPut(root.right);
    }

    public static void main(String[] args) {
        RbTree rbTree = new RbTree();
        Node root = NIL;
        Scanner scanner = new Scanner(System.in);
        System.out.println("Enter array: ");
        while (true) {
            String[] strings = scanner.nextLine().split(", ");
            for (String string : strings) {
                int key = Integer.parseInt(string);
                root = rbTree.insert(root, key);
                System.out.println("--------AVL tree print-------");
                outPut(root);
                System.out.println();
                System.out.println("--------AVL tree print done -------");
            }
        }
    }

}

测试结果:

Enter array: 
1, 2, 3, 4, 5, 6, 7, 8, 9
--------AVL tree print-------
1, 1, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 1, 0, 2
0, 2, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 2, 1, 3
1, 1, 0, 0
1, 3, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 2, 1, 3
1, 1, 0, 0
1, 3, 0, 4
0, 4, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 2, 1, 4
1, 1, 0, 0
0, 4, 3, 5
1, 3, 0, 0
1, 5, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 2, 1, 4
1, 1, 0, 0
0, 4, 3, 5
1, 3, 0, 0
1, 5, 0, 6
0, 6, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 4, 2, 6
1, 2, 1, 3
1, 1, 0, 0
1, 3, 0, 0
1, 6, 5, 7
1, 5, 0, 0
1, 7, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 4, 2, 6
1, 2, 1, 3
1, 1, 0, 0
1, 3, 0, 0
1, 6, 5, 7
1, 5, 0, 0
1, 7, 0, 8
0, 8, 0, 0

--------AVL tree print done -------
--------AVL tree print-------
1, 4, 2, 6
1, 2, 1, 3
1, 1, 0, 0
1, 3, 0, 0
1, 6, 5, 8
1, 5, 0, 0
0, 8, 7, 9
1, 7, 0, 0
1, 9, 0, 0

--------AVL tree print done -------
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值