Java 实现 红黑树(通俗易懂)

目录

一.概念

二.定义节点

三.插入操作

四.总结


一.概念

红黑树本质是一种二叉搜索树,是AVL树的特化版本。其也是通过一些条件限制来达到相对平衡的目的。

定义:

1.节点是红色黑色

2.根节点是黑色

3.所有叶子节点都是黑色

4.每个红色节点的两个子节点都是黑色,不能有两个连续的红色节点;

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

红黑树必须满足上面的五条规则,满足上述的五条规则即可实现:最长路径最多是最短路径的2倍 这一平衡。

二.定义节点

这里定义的节点与二叉搜索树大致相同,多了一个颜色标识。这里的颜色大家可以使用自己熟悉的方法,像布尔变量,定义一个flag.....都行,这一使用的是枚举。

一个节点在初始的时候是红色的,这是定义导致的。定义第五条规定了每条路上的黑色节点数要相同,如果插入一个黑色节点,那么要调整的节点很多很复杂。只要插入了,百分之一百要调整(数都变了)。如果插入红色节点,要调整的内容小很多,有时甚至不用调(parent节点是黑色)。

static class TreeNode{
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode parent;
    public Color color;

    public TreeNode(int val){
        this.val=val;
        this.color=Color.RED;
    }
}

public TreeNode root;

三.插入操作

插入节点的操作与二叉搜索树的插入没有区别,区别是后面对颜色的调整。

颜色调整的准则就是上面的五条定义。

这里可以进行分类讨论:

1.插入的是根节点,直接染上黑色返回即可;

2.插入点的parent是黑色,由于插入点默认是红色,所以不影响定义5,可以直接结束;

3.插入点的parent是红色,由于parent是红色,可得grandparent一定是黑色,否则就不满足性质4了。所以说我们只需要讨论插入点的uncle节点是什么颜色的即可。这里可以分出三种:

第一种:uncle是红色的;第二种:uncle是黑色或空的。

如果是第一种,让该点的parent节点和uncle节点颜色变黑,grandparent节点的颜色变红,然后让cur(插入点,也可能是循环过程中的点)指向grandparent,让parent指向grandparent的parent,再次进入循环。

如果是第二种,情况复杂很多,这里要画图方便理解。

这是cur是parent的左子树的情况:

上图是循环过程中的某一次,其可能是完整的树也可能是某一颗子树。

我们可以先染色后右旋(也可以先右旋后染色,这个无所谓)。将grandparent染成红色,parent染成黑色:

染完色后要对grandparent为根的树进行右旋:

这时树就平衡了。

还有一种是cur是parent的右树的时候:

这种情况其实是上一种的变种,将以parent为根的树进行左旋:

是不是很熟悉,如果不熟悉去看一下上面cur是parent左子树的开始状态,不正是跟这个图的颜色一样,只需将cur和parent交换一下,就可以转化成cur是parent左子树这种情况了。

这里我们忽略了一种情况,如果我们不小心将根节点改成红色怎么办?

这里我们最后直接将根节点变成黑色的就行。原因:定义2。可行性:任意一条路径都经过根节点,所以染黑不影响平衡的规则。

除了上面的这种情况,还有一种就是对称情况,将左右反转过来。这种情况与上面思路相同,只需将上面涉及左右的节点换一下相反的即可,这里就不过多赘述了。

public boolean insert(int val){
    TreeNode node =  new TreeNode(val);
    if(root == null){
        root=node;
        root.color=Color.BLACK;
        //插入成功,直接结束
        return true;
    }

    //寻找插入的位置
    TreeNode cur=root;
    TreeNode parent=null;
    while(cur!=null){
        if(cur.val<val){
            parent=cur;
            cur=cur.right;
        }else if(cur.val>val){
            parent=cur;
            cur=cur.left;
        }else{
            //已经有这个点了
            return false;
        }
    }

    //开始插入
    cur=node;
    node.parent=parent;
    if(parent.val>val){
        parent.left=cur;
    }else{
        parent.right=cur;
    }

    //调整红黑树
    if(parent.color==Color.BLACK){
        return true;
    }

    while(parent!=null && parent.color==Color.RED){
        //父节点为红,祖父节点一定是黑,只需讨论叔节点
        TreeNode grandParent = parent.parent;
        TreeNode uncle = null;
        if(parent==grandParent.left){
            uncle = grandParent.right;

            if(uncle!=null && uncle.color==Color.RED){
                //叔节点为红
                parent.color=uncle.color=Color.BLACK;
                grandParent.color=Color.RED;

                cur=grandParent;
                parent=cur.parent;
            }else{
                //叔节点不为红或为空

                //cur是parent的右孩子
                if(cur==parent.right){
                    rotateLeft(parent);
                    TreeNode tem=parent;
                    parent=cur;
                    cur=tem;
                }

                grandParent.color=Color.RED;
                parent.color=Color.BLACK;
                rotateRight(grandParent);
            }
        }else{
            uncle = grandParent.left;

            if(uncle!=null && uncle.color==Color.RED){
                //叔节点为红
                parent.color=uncle.color=Color.BLACK;
                grandParent.color=Color.RED;

                cur=grandParent;
                parent=cur.parent;
            }else{
                //叔节点不为红或为空

                //cur是parent的左孩子
                if(cur==parent.left){
                    rotateRight(parent);
                    TreeNode tem=parent;
                    parent=cur;
                    cur=tem;
                }

                grandParent.color=Color.RED;
                parent.color=Color.BLACK;
                rotateLeft(grandParent);
            }
        }
    }

    root.color=Color.BLACK;
    return true;
}

以上就是插入操作的全部内容了。

四.总结

红黑树是相对平衡的,没有像AVL树有很多插入删除操作,性能相对AVL树更强,实际应用也很多,像Java中TreeMap底层就用的是红黑树。

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值