java红黑树的理解与实现

红黑树,一个很牛x的数据结构,作为目前JDK的hashmap的底层,是一个兼顾了空间和时间的完美二叉查找树,在AVL的平衡性上做出了巨大改进。首先它的本质是一种特殊的AVL树,祖父辈是二叉排序树,就是那个左子节点必定小于等于它,右子节点必定大于它的树。
首先要了解红黑树就要从他的基本性质说起,
1.根节点必定为黑色
2.不能有两个连接的红色节点
3.节点颜色只能为红或者黑
4.任意节点到每个叶子节点途径的黑色节点数都是相同的
5.所有的子节点(NIL)必定为黑色

第一个性质,硬性规定,没有什么好说的
第二个性质,红色不能相连,如果相连被允许,那么该树将无法确定平衡方法
第三个性质,红黑树红黑树,难道还能有绿色吗?
第四个性质,这是红黑树设计时所考虑到的独特的平衡性,即去掉所有红色节点后,该树的黑色叶子结点会在同一层
第五个性质,为了更好的观摩性质4提出的(实际不考虑也可以)
那么性质讲完了,就该讲讲操作了,
首先插入操作,插入时需要考虑很多东西,先是按照二叉排序树方式插入,插入时规定每个节点颜色为红色以便于自我平衡,最后再进行自我平衡。首先要判断是否为根节点,然后再进行迭代操作进行插入。最后再自我平衡。

public class RBT<T>{
    private Node root;
    private int R=0;
    private int B=1;

    //内部类,节点
    class Node{
        Node left;
        Node right;
        Node parent;
        private T key;
        int color;

        public Node(T key) {
            this.color=R;
            this.key = key;
        }
    }
   //插入操作
    public void insert(T key)
    {
        Node node=new Node(key);
        if(root==null)
        {
            root=node;
            root.color=B;
            return;
        }
        Node parent=root;
        Node son=null;
        if((Integer)(node.key)<=(Integer)(parent.key))
        {
            son=parent.left;
        }
        else {
            son=parent.right;
        }
        while (son!=null)
        {
            parent=son;
            if((Integer)(node.key)<=(Integer)(parent.key))
            {
                son=parent.left;
            }
            else {
                son=parent.right;
            }
        }
        if(key <=parent.key){
            parent.left = node;
        }else {
            parent.right = node;
        }
        node.parent=parent;

        insertPolicy(node);

    }	

自我平衡,这是红黑树实现的关键部分,这边先要了解三个原子操作,变色,左旋,右旋。
1.变色操作,条件叔叔和父亲都为红色,当然它的自身也为红色。那么就能进行变色操作。
操作核心,将父亲和叔叔变黑,爷爷节点(祖父)变成红色,这个操作不一定就能变好,所以然后把当前节点设置为爷爷节点,进行下一步操作。
2.左旋操作,直接说可能说不清直接上图(图片来源网络,侵权删左旋
伪代码(不考虑父节点的情况下)
right=node.right;
node.parent=right;
node.right=right.left;
right.left=node;
具体操作为(结点的右树变为父节点的子节点),然后将右子节点的左树挂给当前结点做右树,让右子结点成为当前节点的父亲结点,当前结点为右子结点的左树。
3.右旋操作,继续上图
在这里插入图片描述
伪代码(仍然是不考虑父节点的情况)
left=node.left;
node.left=left.right;
left.right=node;
node.parent=left;
接下来再讲讲具体的平衡策略,首先我们要分清楚左倾和右倾的情况,一般而言建立左倾是最方便的,但是操作复杂度会增加,最好的方式就是让程序自己去平衡是要左倾还是右倾。
来看一下具体操作吧,
1.变色的约束条件,上面写了。

2.父结点为左子树的左旋条件,当前结点在父节点右侧,当前父节点为红色,叔叔结点是黑色,左旋时以父节点为旋转结点,之后将父节点视作当前结点,进行下一步操作

3.父结点亲左子树的右旋条件,父节点为红,叔叔为黑,
然后先变色再旋,父节点为黑,爷爷节点为红,然后以爷爷节点为旋转结点。

以上是父亲是祖父节点左子节点时候的情况,那么父节点为右子结点呢,很简单我们进行对称操作就行。
具体操作:
1.操作不变,2操作的判定改为当前结点为父节点的左节点,左旋改为以父节点右旋,3操作右旋改左旋,其他全部不改变。我们发现进行操作的前提必然是父节点为红,所以我们进行代码优化,将这一步作为大条件。
然后我们直接上代码。


//平衡策略
public void insertPolicy(Node node)
    {
        Node father,grandpa;
        while((father=node.parent)!=null&&father.color==R)
        {
            grandpa=father.parent;
            if(grandpa.left==father)
            {
                Node uncle=grandpa.right;
                //叔叔为红,父亲为红就进行变色操作
                if(uncle!=null&&uncle.color==R)
                {
                    father.color=B;
                    uncle.color=B;
                    grandpa.color=R;
                    node=grandpa;
                    continue;
                }
                //左旋
                if(node==father.right)
                {
                    leftRotate(father);
                    Node tmp=node;
                    node=father;
                    father=tmp;
                }
                father.color=B;
                grandpa.color=R;
                rightRotate(grandpa);
            }
            else{//父节点在右
                Node uncle=grandpa.left;
                if(uncle!=null&&uncle.color==R)
                {
                    father.color=B;
                    uncle.color=B;
                    grandpa.color=R;
                    node=grandpa;
                    continue;
                }
                if(node==father.left)
                {
                    rightRotate(father);
                    Node tmp=node;
                    node=father;
                    father=tmp;
                }
                father.color=B;
                grandpa.color=R;
                leftRotate(grandpa);
            }
        }
        root.color=B;
    }
//左旋
    private void leftRotate(Node node)
    {
        Node right=node.right;
        Node parent=node.parent;
        if(parent==null)
        {
            root=right;
            right.parent=null;
        }
        else {
            if(parent.left!=null&&node==parent.left)
            {
                parent.left=right;
            }else parent.right=right;
            right.parent=parent;
        }
        node.parent=right;
        node.right=right.left;
        if(right.left!=null)
        {
            right.left.parent=node;
        }
        right.left=node;
    }
//右旋
    private void rightRotate(Node node)
    {
        Node left=node.left;
        Node parent=node.parent;
        if(parent==null)
        {
            root=left;
            left.parent=null;
        }
        else {
            if(parent.left!=null&&node==parent.left)
            {
                parent.left=left;
            }
            else {
                parent.right=left;
            }
            left.parent=parent;
        }
        node.parent=left;
        node.left=left.right;
        if(left.right!=null)
        {
            left.right.parent=node;
        }
        left.right=node;

    }
    public void mid(Node node)
    {
        if(node!=null) {
            mid(node.left);
            System.out.println(node.key);
            mid(node.right);
        }
    }
    public void first(Node node)
    {
        if(node!=null)
        {
            System.out.println(node.key);
            first(node.left);
            first(node.right);
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值