每日一题~~~

特性: 性质1. 节点是红色或黑色。

性质2. 根节点是黑色。

性质3.所有叶子都是黑色。(叶子是NUIL节点)

性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5.. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

简单的说: 根黑、叶黑、子黑、黑的数目相同、要么黑要么红

插入:

红黑树--详解插入操作_后端

今天撸了红黑树的插入操作,希望多记忆几次,牢固记忆。

package com.jd.platform.async.algorithm.redblacktree;

/**
 * @author 我就是你
 * @version 1.0
 * @date 2021/6/16 0013 17:13
 */
public class RBTree {
    TreeNode head;            //树的头节点
    //创建红色的默认节点,但是未设置父节点
    public TreeNode getNode(int val) {
        TreeNode defaultNode  = new TreeNode(val);
        defaultNode.color = Color.RED;    //默认为红色
        return defaultNode;
    }
    //打印树
    public void printTree(TreeNode node) {
        if(head==null) {
            System.out.println("树是空的,请确认init()方法已经执行!");
            return ;
        }
        if(node==null) return ;
        else {
            //前序遍历
            System.out.print("节点的值:"+node.val+"    节点的颜色:"+node.color);
            if(node.parent!=null) System.out.println("   节点的父节点:"+node.parent.val);
            else System.out.println("    这是根节点");
            printTree(node.left);
            printTree(node.right);
        }
    }
    /*************************************************minit**************************************************/

    //树的初始化
    public void init(int[] arr) {
        for(int i=0;i<arr.length;++i) {
            insert(head,null,arr[i],-1);
        }
    }
    //inset  开始插入 ,lr为0代表left  lr为1代表right lr为-1表示是根节点
    public void  insert(TreeNode head,TreeNode parent,int i,int lr) {
        if(head==null) {
            TreeNode x = getNode(i);
            x.parent=parent;
            head = x;
            if(lr==1) parent.right = head;
            else if(lr==0) parent.left = head;
            insert1(head);
        }
        else {        //递归插入
            if(i>head.val)    insert(head.right,head,i,1);
            if(i<head.val)    insert(head.left,head,i,0);
        }
    }
    //case1:插入的节点为根节点,将插入节点置为红色,前期x的父节点x.parent必须确定下来
    public void insert1(TreeNode  x) {

        if(x.parent==null) {
            x.color = Color.BLACK;
            head = x;                  //将首节点指向x
            return ;
        } else insert2(x);
    }
    //case2:插入的节点不为根节点
    //且插入的节点的父节点为黑色的,那么红黑树是不用调节的
    public void insert2(TreeNode x) {
        if(x.parent.color==Color.BLACK) return ;
        else insert3(x);
    }
    //case3如果插入节点的父节点为红色 ,违反父子节点都为红色的
    // 如果叔叔节点为红色,只需将叔叔节点和父节点同时设为黑色,同时祖父节点设为红色
    //但这会引入新问题,祖父节点和其自身父节点有可能都为红色,使用尾递归向上上滤
    public void insert3(TreeNode x) {
        TreeNode par = x.parent;    //父节点
        TreeNode gra = par.parent;    //祖父节点
        TreeNode unc = (par==gra.left)? gra.right : gra.left;     //叔叔节点
        if(unc!=null && unc.color==Color.RED) {
            unc.color = Color.BLACK;
            par.color = Color.BLACK;
            gra.color = Color.RED;
            insert1(gra);        //尾递归上滤
        } else insert4(x);
    }
    //case4: 如果叔叔节点为黑色或者null
    public void insert4(TreeNode x) {
        TreeNode par = x.parent;    //父节点
        TreeNode gra = par.parent;    //祖父节点//如果父节点是祖父节点的左节点,但x是父节点的右节点,交换x和其父节点,且x变为其原父节点的父节点
        if(par==gra.left && x==par.right) {
            gra.left = x;
            x.left = par;
            x.parent = gra;
            par.right = null;
            par.parent = x;
            insert5(par);
        }
        //如果父节点是祖父节点的右节点,但是x是父节点的左节点,交换x和其父节点,且x变为祖父节点的右节点
        else if(par==gra.right && x==par.left) {
            gra.right = x;
            x.right = par;
            x.parent = gra;
            par.left = null;
            par.parent = x;
            insert5(par);
        }
        else {
            insert5(x);       //因为这个x节点有可能变为父节点了,所以要在insert5进行判断是否为变换后的父节点
        }
    }

    public void insert5(TreeNode x) {
        TreeNode par = x.parent;    //父节点
        TreeNode gra = par.parent;    //祖父节点
        TreeNode ggra = gra.parent; //祖父节点的父节点
        if(x==par.left) {
            gra.left = par.right;
            par.right = gra;
            par.parent = ggra;
            gra.parent = par;
            if(gra.left!=null) gra.left.parent = gra;        //如果节点不为空更新父节点信息
            //ggra.left = par;
            if(ggra==null) head = par;
            else {
                if(par.val>ggra.val) ggra.right = par;
                else ggra.left = par;
            }
        }
        else if(x==par.right) {
            //if(x.val==12) System.out.println("12的父节点的左节点:"+par.left.val);
            gra.right = par.left;
            par.left = gra;
            par.parent = ggra;
            gra.parent = par;
            if(gra.right!=null) gra.right.parent = gra;            //要更新父节点信息
            if(ggra==null) head = par;   //根节点要重新指向
            else  {
                if(par.val>ggra.val) ggra.right = par;
                else ggra.left = par;
            }
        }
        //颜色变化
        gra.color = Color.RED;
        par.color = Color.BLACK;
    }
    /*************************************************main**************************************************/
    public static void main(String[] args) {
        int[] arr = {5,3,1,7,9,6,15,12,14,13};
        RBTree rbt = new RBTree();
        rbt.init(arr);
        rbt.printTree(rbt.head);
    }

    //红黑树节点
    private class TreeNode{
        Color color=Color.RED;
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
        TreeNode(int value) {
            val = value;
        }
    }
    //树节点枚举类
    private enum Color{
        RED,BLACK;
    }

}

复制代码

总结:

case1:插入的节点为根节点,将插入节点置为红色

case2:插入的节点不为根节点

且插入的节点的父节点为黑色的,那么红黑树是不用调节的

case3如果插入节点的父节点为红色 ,违反父子节点都为红色的

如果叔叔节点为红色,只需将叔叔节点和父节点同时设为黑色,同时祖父节点设为红色

但这会引入新问题,祖父节点和其自身父节点有可能都为红色,使用尾递归向上上滤

case4: 如果叔叔节点为黑色或者null

如果父节点是祖父节点的左节点,但x是父节点的右节点,交换x和其父节点,且x变为其原父节点的父节点

如果父节点是祖父节点的右节点,但是x是父节点的左节点,交换x和其父节点,且x变为祖父节点的右节点