RBTree

二叉查找树

情况1

在这里插入图片描述
此时查找的时间复杂度为 O(logn)

情况2

在这里插入图片描述
此时查找的时间复杂度为 O(n), 不符合二叉查找树的设计的初衷。所以引出了平衡二叉树,左右子树高度差不超过1,可以避免出现线性结构的情况,但是还不够理想。
为什么有了AVL树还会有红黑树

因为AVL树要求左右子树高度差不超过1,这个要求太严格 ,导致插入、删除时
,很大几率破坏高度差为1的规则,进而需要通过左旋、右旋来调整,导致性能大打折扣。
为了解决这个问题,就出现了红黑树。

红黑树

底层数据结构就是一个特殊的二叉查找树
特征

  • 每个节点不是黑色就是红色
  • 不可能有连在一起的红色节点
  • 根节点都是黑色
  • 每个红色节点的两个子节点都是黑色,叶子节点都是黑色的
  • 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点

为了满足红黑树的性质,有三种操作

  1. 改变颜色
  2. 左旋
  3. 右旋

插入

插入节点必须为红色, 保证不破坏黑色数量平衡

情景1, 为空树

插入节点为根节点,并设置为黑色

情景2, 插入节点的key已存在

更新节点

情景3: 插入节点的父节点为黑色

直接插入

情景4: 插入节点的父节点为红色

根据红黑树性质,既然父节点为红色 ,则爷爷节点必为黑色 
情景4.1: U存在且U为红色
1. P和U改为黑色
2. PP改为红色
3. 将PP设置为当前节点,进行后续处理
情景4.2: U不存在或为黑色 ,插入节点的P是PP的左子节点
情景4.2.1: currnet为P的左子节点(LL红)
1.P设为黑色 ,PP为红色
2.对PP右旋

在这里插入图片描述

情景4.2.2: currnet为P的右子节点(LR红)
1.对P左旋
2.P设为current,得到LL红情况
3.按照LL红的情况处理

在这里插入图片描述

情景4.3: U不存在或为黑色 ,插入节点的P是PP的右子节点

在这里插入图片描述

情景4.3.1: currnet为P的右子节点(RR红)
1.P设为黑色 ,PP为红色
2.对PP左旋

在这里插入图片描述

情景4.3.2: currnet为P的左子节点(RL红)
1.对P右旋
2.P设为current,得到RR红情况
3.按照RR红的情况处理

在这里插入图片描述

代码

RBTree.java
/*
 * 1.创建RBTree,定义颜色
 * 2.创建RBNode
 * 3.辅助方法定义 parentOf(node), isRed(node), setRed(node), setBlack(node), inorderPrint(node)
 * 4.左旋方法 leftRotate(node)
 * 5.右旋方法 rightRotate(node)
 * 6.公开插入接口 insert(K key, V value)
 * 7.内部插入接口 insert(RBNode node)
 * 8.修正平衡术插入导致的失衡 insertFixUp(RBNode)
 * 9.测试正确性
 */
public class RBTree<K extends Comparable<K>, V> {
    private final static boolean RED = true;
    private final static boolean BLACK = false;
    private RBNode root = null;

    public RBNode getRoot() {
        return root;
    }

    private RBNode parentOf(RBNode node) {
        if (node != null) {
            return node.parent;
        }
        return null;
    }

    private boolean isRed(RBNode node) {
        if (node != null) {
            return node.color == RED;
        }
        return false;
    }

    private boolean isBlack(RBNode node) {
        if (node != null) {
            return node.color == BLACK;
        }
        return false;
    }

    private void setRed(RBNode node) {
        if (node != null) {
            node.color = RED;
        }
    }

    private void setBlack(RBNode node) {
        if (node != null) {
            node.color = BLACK;
        }
    }

    public void inorderPrint(RBNode node) {
        if (node != null) {
            inorderPrint(node.left);
            System.out.println("Key: " + node.key + ", value:" + node.value);
            inorderPrint(node.right);
        }
    }

    private void leftRotate(RBNode x) {
        RBNode y = x.right;
        x.right = y.left;
        if (y.left != null) {
            y.parent = x;
        }
        y.parent = x.parent;
        if (y.parent == null) {
            root = y;
        } else {
            if (x == x.parent.left) {
                x.parent.left = y;
            } else {
                x.parent.right = y;
            }
        }
        y.left = x;
        x.parent = y;
    }

    private void rightRotate(RBNode y) {
        RBNode x = y.left;
        y.left = x.right;
        if (x.right != null) {
            x.right.parent = y;
        }
        x.parent = y.parent;
        if (x.parent == null) {
            root = x;
        } else {
            if (y == y.parent.left) {
                y.parent.left = x;
            } else {
                y.parent.right = x;
            }
        }
        x.right = y;
        y.parent = x;
    }

    public void insert(K key, V value) {
        RBNode node = new RBNode();
        node.setValue(value);
        node.setKey(key);
        node.setColor(RED);
        insert(node);
    }

    private void insert(RBNode node) {
        // 查找当前node父节点
        RBNode parent = null;
        RBNode x = this.root;
        while (x != null) {
            parent = x;
            int cmp = node.key.compareTo(x.key);
            if (cmp < 0) {
                x = x.left;
            } else if (cmp > 0) {
                x = x.right;
            } else {
                x.setValue(node.value);
                return;
            }
        }
        node.parent = parent;
        if (parent != null) {
            int cmp = node.key.compareTo(parent.key);
            if (cmp < 0) {
                parent.left = node;
            } else {
                parent.right = node;
            }
        } else {
            this.root = node;
        }
        insertFixUp(node);
    }

    private void insertFixUp(RBNode node) {
        this.root.setColor(BLACK);
        RBNode parent = parentOf(node);
        RBNode gParent = parentOf(parent);
        if (parent != null && isRed(parent)) {
            RBNode uncle = null;
            if (parent == gParent.left) { // P是PP的左子节点
                uncle = gParent.right;
                // 4.1 U存在且U为红色
                if (uncle != null && isRed(uncle)) {
                    //1. P和U改为黑色
                    //2. PP改为红色
                    //3. 将PP设置为当前节点,进行后续处理
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(gParent);
                    insertFixUp(gParent);
                    return;
                }
                // 情景4.2: U不存在或为黑色
                if (uncle == null || isBlack(uncle)) {
                    // 情景4.2.1: currnet为P的左子节点(LL红)
                    //1.P设为黑色 ,PP为红色
                    //2.对PP右旋
                    if (node == parent.left) {
                        setBlack(parent);
                        setRed(gParent);
                        rightRotate(gParent);
                        return;
                    }
                    // 情景4.2.2: currnet为P的右子节点(LR红)
                    //1.对P左旋
                    //2.P设为current,得到LL红情况
                    //3.按照LL红的情况处理
                    if (node == parent.right) {
                        leftRotate(parent);
                        insertFixUp(parent);
                        return;
                    }
                }

            } else { // 插入节点的P是PP的右子节点
                uncle = gParent.left;
                // 4.1 U存在且U为红色
                if (uncle != null && isRed(uncle)) {
                    //1. P和U改为黑色
                    //2. PP改为红色
                    //3. 将PP设置为当前节点,进行后续处理
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(gParent);
                    insertFixUp(gParent);
                    return;
                }
                //  U不存在或为黑色
                if (uncle == null || isBlack(uncle)) {
                    //情景4.3.1: currnet为P的右子节点(RR红)
                    //1.P设为黑色 ,PP为红色
                    //2.对PP左旋
                    if (node == parent.right) {
                        setBlack(parent);
                        setRed(gParent);
                        leftRotate(gParent);
                        return;
                    }
                    //情景4.3.2: currnet为P的左子节点(RL红)
                    //1.对P右旋
                    //2.P设为current,得到RR红情况
                    //3.按照RR红的情况处理
                    if (node == parent.left) {
                        rightRotate(parent);
                        insertFixUp(parent);
                        return;
                    }
                }
            }

        }
    }

    static class RBNode<K extends Comparable<K>, V> {
        private RBNode parent;
        private RBNode left;
        private RBNode right;
        private boolean color;
        private K key;
        private V value;

        public RBNode() {
        }

        public RBNode(RBNode parent, RBNode left, RBNode right, K key, V value) {
            this.parent = parent;
            this.left = left;
            this.right = right;
            this.key = key;
            this.value = value;
        }

        public RBNode getParent() {
            return parent;
        }

        public void setParent(RBNode parent) {
            this.parent = parent;
        }

        public RBNode getLeft() {
            return left;
        }

        public void setLeft(RBNode left) {
            this.left = left;
        }

        public RBNode getRight() {
            return right;
        }

        public void setRight(RBNode right) {
            this.right = right;
        }

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }

        public boolean getColor() {
            return this.color;
        }

        public boolean isColor() {
            return color;
        }

        public void setColor(boolean color) {
            this.color = color;
        }
    }
}
TreeOperation.java
// TreeOperation.java
public class TreeOperation {
    /*
         Example of the structure of the tree:
              1
            /   \
          2       3
         / \     / \
        4   5   6   7
    */

    // the number of layers used to get the tree
    public static int getTreeDepth(RBTree.RBNode root) {
        return root == null ? 0 : (1 + Math.max(getTreeDepth(root.getLeft()), getTreeDepth(root.getRight())));
    }


    private static void writeArray(RBTree.RBNode currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
        // Ensure that the input tree is not empty
        if (currNode == null) return;
        // First save the current node to a two-dimensional array
        res[rowIndex][columnIndex] = String.valueOf(currNode.getKey()) +  "-" + (currNode.isColor() ? "R": "B" ) + "";

        // Calculate the current layer in the tree
        int currLevel = ((rowIndex + 1) / 2);
        // If it reaches the last level, it will return
        if (currLevel == treeDepth) return;
        // Calculate the interval between each element from the current line to the next line (the interval between the column index of the next line and the column index of the current element)
        int gap = treeDepth - currLevel - 1;

        // judge the left son, if there is a left son, record the corresponding "/" and the value of the left son
        if (currNode.getLeft() != null) {
            res[rowIndex + 1][columnIndex - gap] = "/";
            writeArray(currNode.getLeft(), rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
        }

        // Make a judgment on the right son. If there is a right son, record the value of the corresponding "\" and the right son.
        if (currNode.getRight() != null) {
            res[rowIndex + 1][columnIndex + gap] = "\\";
            writeArray(currNode.getRight(), rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
        }
    }


    public static void show(RBTree.RBNode root) {
        if (root == null) System.out.println("EMPTY!");
        // Get the depth of the tree
        int treeDepth = getTreeDepth(root);

        // The width of the last line is 2 (n - 1) and the power is 3, plus 1
        // as the width of the entire two-dimensional array
        int arrayHeight = treeDepth * 2 - 1;
        int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
        // Use an array of strings to store the elements that should be displayed at each location
        String[][] res = new String[arrayHeight][arrayWidth];
        // Initialize the array, the default is a space
        for (int i = 0; i < arrayHeight; i ++) {
            for (int j = 0; j < arrayWidth; j ++) {
                res[i][j] = " ";
            }
        }

        // Recursive processing of the entire tree from the root node
        // res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0');
        writeArray(root, 0, arrayWidth/ 2, res, treeDepth);

        // At this point, all the elements that need to be displayed have been stored in a two-dimensional array, spliced ​​and printed.
        for (String[] line: res) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < line.length; i ++) {
                sb.append(line[i]);
                if (line[i].length() > 1 && i <= line.length - 1) {
                    i += line[i].length() > 4 ? 2: line[i].length() - 1;
                }
            }
            System.out.println(sb.toString());
        }
    }
}

MyTest,java
import java.util.Scanner;

public class MyTest {

    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        RBTree<Integer, Object> rbt = new RBTree();
        while (true) {
            System.out.println("Please input key");
            String key = scanner.next();
            rbt.insert(new Integer(key), null);
            TreeOperation.show(rbt.getRoot());
        }
    }
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值