红黑树的创建

x33g5p2x  于2022-02-11 转载在 其他  
字(5.3k)|赞(0)|评价(0)|浏览(311)

2-3-4树

红黑树由2-3-4树演变而来。在开始红黑树之前我先介绍一下2-3-4树。2-3-4树是一种阶为4的B树。它是一种自平衡的数据结构,可以保证在O(lgn)的时间内完成查找、插入和删除操作。它主要满足以下性质:
1、2-3-4树所有的叶子结点都在同一层
2、结点只能是2结点或3结点或4结点

  • 2结点:要么有2个子结点,要么没有子结点
  • 3结点:要么有3个子结点,要么没有子结点
  • 4结点:要么有4个子结点,要么没有子结点

2-3-4树如何转换成红黑树
2结点可以转化成黑结点
3结点有两种转变模式所以按不同方式的转化会得到不同的红黑树
但无论按哪种方式父结点都是黑色(上黑下红)
4结点可以转化成一个黑的父结点和两个红色子结点

红黑树

红黑树(Red Black Tree) 是一种自平衡二叉查找树,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。
红黑树具有以下特点:
1、每个结点要么是黑色的要么是红色的
2、根结点都是黑丝的
3、每个叶子结点都是黑色的(叶子结点为null)
4、每个红色结点的两个子节点都是黑色的(不存在两个连续的红结点)
5、从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点

红黑树添加结点思路分析

1、按二叉排序树的规则添加结点
2、红黑树的平衡调整:旋转+变色
当我们添加结点后有以下8中情况会导致红黑树失去平衡

注意:如果上图12结点是根结点还需要将12变回黑色

代码实现

public class Node{
    private Node parent; //父结点
    private Node left; //左子结点
    private Node right; //右子结点
    private boolean color; //颜色
    private int key;
    private String value;
    
    public Node(int key, String value, Node parent) {
        this.key = key;
        this.value = value;
        this.parent= parent;
    }
    /*中序遍历*/
    public void midDisplay(){
        if (this.left != null) {
            this.left.midDisplay();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.midDisplay();
        }
    }
	getter and setter 方法
    @Override
    public String toString() {
        return "Node{" +
                "color=" + (color ? "黑" : "红") +
                ", key=" + key +
                ", value=" + value +
                ",左子结点key=" +(left==null ? "null" : left.key) +
                ",右子结点key=" +(right==null ? "null" : right.key) +
                '}';
    }
}
public class BRTree  {
    /*false表示红色 true表示黑色*/
    private static final boolean RED = false;
    private static final boolean BLACK = true;
    private Node root;

    /*中序遍历*/
    public void midShow(){
        if (this.root != null){
            this.root.midDisplay();
        }else{
            System.out.println("二叉树为空");
        }
    }
    /*左旋*/
    private void leftRotate(Node node){
        if (node != null) {
            Node right = node.getRight();
            node.setRight(right.getLeft());
            if (node.getLeft() != null) {
                right.getLeft().setParent(node);
            }
            right.setParent(node.getParent());
            if (node.getParent() == null){
                root = right;
            }else if (node.getParent().getLeft() == node){
                node.getParent().setLeft(right);
            }else{
                node.getParent().setRight(right);
            }
            right.setLeft(node);
            node.setParent(right);
        }
    }

    /*右旋*/
    private void rightRotate(Node node){
        if (node != null) {
            Node left = node.getLeft();
            node.setLeft(left.getRight());
            if (node.getRight() != null) {
                left.getRight().setParent(node);
            }
            left.setParent(node.getParent());
            if (node.getParent() == null){
                root = left;
            }else if (node.getParent().getLeft() == node){
                node.getParent().setLeft(left);
            }else{
                node.getParent().setRight(left);
            }
            left.setRight(node);
            node.setParent(left);
        }
    }

    /**
     * 新增结点
     *  1、普通的二叉排序树的结点插入
     *  2、红黑树的平衡:旋转+变色
     * @param key
     * @param value
     */
    public void put(int key,String value){
        Node temp = this.root;
        if (temp == null) {
            /*红黑树为空*/
            root = new Node(key,value,null);
            root.setColor(BLACK);
            return;
        }
        /*记录查找插入的位置*/
        int cmp = 0;
        /*记录父节点*/
        Node parent = null;
        /*找到插入结点的位置*/
        while(temp != null){
            parent = temp;
            cmp = key - temp.getKey();
            if (cmp < 0) {
                /*key值比当前结点的值小 从左子树继续寻找*/
                temp = temp.getLeft();
            }else if (cmp > 0){
                /*key值比当前结点的值大 从右子树继续寻找*/
                temp = temp.getRight();
            }else{
                temp.setValue(value);
                return;
            }
        }
        Node addNode = new Node(key,value,parent);
        if (cmp < 0){
            parent.setLeft(addNode);
        }else {
            parent.setRight(addNode);
        }
        /*调整红黑树*/
        adjustBRTree(addNode);
    }

    /*获取node结点的颜色*/
    private boolean colorOf(Node node){
        return node == null ? BLACK : node.isColor();
    }
    /*设置node结点的颜色*/
    private void setColor(Node node,boolean color){
        if (node != null){
            node.setColor(color);
        }
    }
    /*获取node结点的父节点*/
    private Node parentOf(Node node){
        return node ==null ? null : node.getParent();
    }
    /*获取node结点的左子结点*/
    private Node leftOf(Node node){
        return node == null ? null : node.getLeft();
    }
    /*获取node结点的右子结点*/
    private Node rightOf(Node node){
        return node == null ? null :node.getRight();
    }

    /**
     * node 添加的结点
     * @param node
     */
    private void adjustBRTree(Node node) {
        /*插入的结点都是红色结点*/
        setColor(node,RED);
        /*如果当前结点的父节点是红结点就需要调整红黑树*/
        while(node != null && node != root && parentOf(node).isColor() == RED) {
             /*parentOf(parentOf(node))为什么不会空指针?
                只有父结点是红节点才会进入循环 而红结点不可能是根结点
                所以当前结点一定有爷爷结点
             */
             if (parentOf(node) == parentOf(parentOf(node)).getLeft()){
                 /*当前结点的父节点是爷爷结点的左子结点*/
                 //叔叔结点
                 Node uncleNode = rightOf(parentOf(parentOf(node)));
                 if(colorOf(uncleNode) == RED){
                     /*叔叔结点存在*/
                     /*将父结点和叔叔结点的颜色设置成黑色*/
                     setColor(parentOf(node),BLACK);
                     setColor(uncleNode,BLACK);
                     /*将爷爷结点的颜色设置成红色*/
                     setColor(parentOf(parentOf(node)),RED);
                     node = parentOf(parentOf(node));
                 }else{
                     /*没有叔叔结点*/
                     if (node == rightOf(parentOf(node))){
                         /*如果插入的结点是父节点的右结点 那么需要双旋*/
                         /*根据父结点做左旋操作*/
                         leftRotate(parentOf(node));
                     }
                     setColor(parentOf(node),BLACK);
                     setColor(parentOf(parentOf(node)),RED);
                     rightRotate(parentOf(parentOf(node)));
                 }
             }else{
                 /*当前结点的父节点是爷爷结点的左子结点*/
                 Node uncleNode = leftOf(parentOf(parentOf(node)));
                 if(colorOf(uncleNode) == RED){
                     setColor(parentOf(node),BLACK);
                     setColor(uncleNode,BLACK);
                     /*将爷爷结点的颜色设置成红色*/
                     setColor(parentOf(parentOf(node)),RED);
                     node = parentOf(parentOf(node));
                 }else{
                     /*没有叔叔结点*/
                     if (node == leftOf(parentOf(node))){
                         /*根据父结点做右旋操作*/
                         rightRotate(parentOf(node));
                     }
                     setColor(parentOf(node),BLACK);
                     setColor(parentOf(parentOf(node)),RED);
                     leftRotate(parentOf(parentOf(node)));
                 }
             }
        }
        /*根结点都为黑色*/
        setColor(root,BLACK);
    }
}

测试类

public class BRTreeTest {
    public static void main(String[] args) {
        BRTree brTree = new BRTree();
        brTree.put(1,"张三");
        brTree.put(2,"李四");
        brTree.put(3,"王五");
        brTree.put(4,"赵六");
        brTree.put(5,"田七");
        brTree.put(6,"陈八");
        brTree.midShow();
    }
}

运行结果

Node{color=黑, key=1, value=张三,左子结点key=null,右子结点key=null}
Node{color=黑, key=2, value=李四,左子结点key=1,右子结点key=4}
Node{color=黑, key=3, value=王五,左子结点key=null,右子结点key=null}
Node{color=红, key=4, value=赵六,左子结点key=3,右子结点key=5}
Node{color=黑, key=5, value=田七,左子结点key=null,右子结点key=6}
Node{color=红, key=6, value=陈八,左子结点key=null,右子结点key=null}

Process finished with exit code 0

验证一下

相关文章