红黑树(一)

昨天已经写了一个二叉树,不过那个是一个非平衡二叉树,对于插入乱序的数据那是有一定的优势的,不过当插入的数据都是有序的(顺序或者是逆序)那执行的效果就不怎么好了。那有没有办法去解决这个问题,有,那就是今天要讲得红黑树,也就是平衡二叉树。

红黑树的特征:
  1. 节点都有颜色
  2. 在插入和删除的过程中,要遵循保持这些颜色的不同排列的规则
红黑规则

当插入(或者删除)一个新节点时,必须要遵循的一定规则,下面介绍一下这些规则:

  • 每个节点不是红色的就是黑色的。
  • 根节点总是黑色的。
  • 如果节点时红色的,则它的子节点必须是黑色的(反之倒不一定必须为真)
  • 从根倒叶节点或空节点的每条路径,必须包含相同数量的黑色节点。

了解了上面的规则,现在我们就开始去写代码吧。(由于之前没有学好红黑树哪些规则,所以后面的删除我就省略了,下一次博客在写红黑树的删除,今天就写红黑树的添加元素)

/**
 * 节点树的接口
 * @author chenjingbin
 *
 * @param <E> 可以比较的类
 */
public interface Tree<E extends Comparable<E>> {
    /**
     * 查找是否有这个元素
     * @param e
     * @return 
     */
    boolean search(E e);
    /**
     * 插入一个元素
     * @param e
     * @return
     */
    boolean insert(E e);
    /**
     * 删除一个元素
     * @param e
     * @return
     */
    boolean delete(E e);
    /**
     *中序遍历
     */
    void inorder();
    /**
     * 前序遍历
     */
    void preorder();
    /**
     * 后序遍历
     */
    void postorder();
    /**
     * 有多少个节点
     * @return
     */
    int getSize();
    /**
     * 判断是否为空
     * @return
     */
    boolean isEmpty();
    /**
     * 清空树
     * @return
     */
    boolean clear();
}
//拿上一次定义好的接口去使用那就可以了


/**
 * 红黑树RABTree
 * @author chenjingbin 
 *红黑树是平衡二叉树,比较复杂的操作那就是在插入和删除,会使用到左旋转和右旋转
 * @param <E>
 */
public class RABTree<E extends Comparable<E>> implements Tree<E> {
    public static final boolean RED = false;
    public static final boolean BLACK = true;
    public RABNode<E> root;
    public int size ;
    @Override
    public boolean search(E e) {
        // TODO Auto-generated method stub
        RABNode<E> current = root;
        while (current != null) {
            if(e.compareTo(current.element)<0)
                current = current.left;
            else if(e.compareTo(current.element)>0){
                current = current.right;
            }else {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean insert(E e) {
        // TODO Auto-generated method stu
        RABNode<E> t=root;
        if(t==null){
            root = new RABNode<E>(e, null);
            size++;
            return true;
        }else {
            RABNode<E> parent = null;
            RABNode<E> current = root;
            while(current !=null){
                if(e.compareTo(current.element)<0){
                    parent = current;
                    current = current.left;
                }else if(e.compareTo(current.element)>0){
                    parent = current;
                    current = current.right;
                }else 
                    //这里面相同的元素那就不用插入,直接返回false
                    return false;
            }
            RABNode<E> node = new RABNode<E>(e, parent);
            if(e.compareTo(parent.element)<0)
                parent.left = node;
            else 
                parent.right =node;
            //上面的代码和昨天的一样,不一样的地方是后面这个函数,因为插入一个节点那就需要去修改使得这棵树平衡
            fixAfterInsertion(node);
            size++;
            return true;
        }
    }
        /**
         * case1:当前节点的父结点是红色,且当前 节点的祖父节点的另一个子节点(叔叔节点)也是红色
         * 处理策略:(1)将父结点设为黑色(2)将叔叔节点设置为黑色(3)将祖父节点设置为红色(4)将祖父节点设置为“当前节点”(红色节点):即,之后继续对当前节点进行操作。
         * case2:当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子
         * 处理策略:(1)将父结点作为新的当前节点(2)以新的当前节点为支点进行左旋转
         * case3:当前节点的父结点是红色,叔叔节点是黑色,且当前节点是其父结点的左孩子
         * 处理策略:(1)将父结点设置为黑色(2)将祖父节点设置为红色(3)以祖父节点为支点进行右旋转
         */
    private void fixAfterInsertion(RABNode<E> x){
        x.color = RED ;
        while(x!= null && x!=root &&x.parent.color ==RED){
            if(parentOf(x) == leftOf(parentOf(parentOf(x)))){
                RABNode<E> y = rightOf(parentOf(parentOf(x)));
                if(colorOf(y)==RED){
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                }else{
                    if(x==rightOf(parentOf(x))){
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                        setColor(parentOf(x), BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        rotateRight(parentOf(parentOf(x)));
                }
            }else{
                RABNode<E> y = leftOf(parentOf(parentOf(x)));
                if(colorOf(y) == RED){
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                }else{
                    if(x == leftOf(parentOf(x))){
                        x= parentOf(x);
                        rotateRight(x);
                    }setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK ;
    }
    @Override
    public boolean delete(E e) {
        // TODO Auto-generated method stub
        /**
         * 第一步:将红黑树当做一颗二叉查找树,将节点删除
         * 这里面有三种情况:1被删除节点没有孩子,即为叶子节点,那么直接删除
         * 2被删除节点只有一个儿子,那么,直接删除该节点,并用该节点的唯一子节点去替换他的位置
         * 3被删除节点有两个孩子,那么先找出他的后继节点,然后把后继节点的内容复制给该节点的内容
         * 第二步:通过旋转和重新着色等一系列来修正该树,使之重新成为一颗红黑树
         */
        //没有写完,后面再写
        return false;
    }
    //这个方法是拿到比t大一点的节点方法
    private  RABNode<E> successor(RABNode<E> t){
        if(t== null){
            return null;
        }else if(t.right!=null){
            //说明t的右边有节点
            RABNode<E> p = t.right;
            while(p.left!=null){
                p= p.left;  
            }
            return p;
        }else{
            RABNode<E> p = t.parent;
            RABNode<E> ch=t;
            while(p!=null&&p.left==ch){
                ch = p;
                p=p.parent;
            }
            return p;
        }
    }
        /**
         * case1:x是黑+黑节点,x 的兄弟节点是红色(此时x的父结点和x的兄弟节点的子节点都是黑色)
         * 处理策略:
         *1将x的兄弟节点设置为黑色2将x的父结点设置为红色3对x的父结点进行左旋转4左旋转后,重新设置x的兄弟节点
         *case2:x是黑+黑节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。
         *处理策略:1将x的兄弟节点设置为红色2设置x的父结点为新的x节点
         *case3:x是黑+黑节点,x的兄弟节点是黑色,x的兄弟节点的左孩子是红色,右孩子是黑色
         *处理策略:1将x兄弟节点的左孩子设置为黑色,2将x的兄弟节点设置为红色3对x的兄弟节点进行右旋转4右旋后重新设置x的兄弟节点
         *case4:x是黑+黑节点,x的兄弟节点是黑色,x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色
         *处理策略:1将x的父结点颜色赋值给x的兄弟节点2将x父结点设置为黑色3将x兄弟节点的右子节点设置为黑色4对x的父结点进行左旋转5设置x为根节点
         */
    private void fixAfterDeletion(RABNode<E> p){
        //没有写完,后面再写
    }
    @Override
    public void inorder() {
        // TODO Auto-generated method stub
        inorder(root);
    }
    private void inorder(RABNode<E> root) {
        // TODO Auto-generated method stub
        if(root== null)return;
        else{
            inorder(root.left);
            System.out.println(root.element+"");
            inorder(root.right);
        }
    }
    @Override
    public void preorder() {
        // TODO Auto-generated method stub
        preorder(root);
    }

    private void preorder(RABNode<E> root) {
        // TODO Auto-generated method stub
        if(root== null)return;
        else{
            System.out.println(root.element+"");
            inorder(root.left);
            inorder(root.right);
        }
    }

    @Override
    public void postorder() {
        // TODO Auto-generated method stub
        postorder(root);
    }

    private void postorder(RABNode<E> root2) {
        // TODO Auto-generated method stub
        if(root== null)return;
        else{
            inorder(root.left);
            inorder(root.right);
            System.out.println(root.element+"");
        }
    }

    @Override
    public int getSize() {
        // TODO Auto-generated method stub
        return size;
    }

    @Override
    public boolean isEmpty() {
        // TODO Auto-generated method stub
        return size == 0;
    }

    @Override
    public boolean clear() {
        // TODO Auto-generated method stub
        size = 0;
        root = null;
        return true;
    }
    /**
     * 根据e去得到节点
     * @param e
     * @return
     */
    public RABNode<E> getNode(E e){
        RABNode<E> current = root;
        while (current != null) {
            if(e.compareTo(current.element)<0)
                current = current.left;
            else if(e.compareTo(current.element)>0){
                current = current.right;
            }else {
                return current;
            }
        }
        return null;
    }
    //向下执行的时候会执行颜色变换,也就是黑色节点有两个红色节点时需要执行颜色变换。
    /**
     * 得到颜色
     * @param p
     * @return
     */
    private  boolean colorOf(RABNode<E> p){
        return p==null?BLACK:p.color;
    }
    /**
     * 获取父结点
     * @param p
     * @return
     */
    private   RABNode<E> parentOf(RABNode<E> p){
        return p==null?null:p.parent;
    }
    /**
     * 获得左节点
     * @param p
     * @return
     */
    private  RABNode<E> leftOf(RABNode<E> p){
        return p==null?null:p.left;
    }
    /**
     * 获得右节点
     * @param p
     * @return
     */
    private  RABNode<E> rightOf(RABNode<E> p){
        return p==null?null:p.right;
    }
    /**
     * 设置颜色
     * @param p
     * @param c
     */
    private  void setColor(RABNode<E> p ,boolean c){
        if(p!=null)
            p.color = c;
    }
    /**
     * 左旋转
     * @param p
     */

    private  void  rotateLeft( RABNode<E> p){
        if( p != null  ){
            RABNode<E> r = p.right;
            p.right = r.left;
            if(r.left != null){
                r.left.parent = p;
            }
            r.parent = p.parent;
            if(p.parent == null){
                root = r;
            }else if(p.parent.left ==p)
                p.parent.left = r;
            else 
                p.parent.right = r;
            r.left = p;
            p.parent = r ;
        }
    }
    /**
     * 右旋转
     * @param p
     */
    private void rotateRight(RABNode<E> p){
        if( p != null){
            RABNode<E> l = p.left;
            p.left =l.right;
            if(l.right!= null)
                l.right.parent = p;
            l.parent = p.parent;
            if(p.parent == null)
                root = l;
            else if(p.parent .left ==p)
                p.parent .left = l ;
            else 
                p.parent .right = l;
            l.right = p;
            p.parent = l;
        }
    }
    /**
     * 定义红黑树节点类
     * @author chenjingbin
     *
     * @param <E>
     */
     static class RABNode<E extends Comparable<E>>{
        E element;
        RABNode<E> right = null;
        RABNode<E> left = null;
        RABNode<E> parent;
        boolean color = BLACK;
        public RABNode(E e,RABNode<E> parent){
            element = e;
            this.parent =parent;
        }
    }
}

上面的代码,删除部分没有写,因为还有点搞不懂,那过几天才去写,后面一定会补上删除代码的部分。在插入节点之前那是跟二叉查找树一样的,不同的是在插入之后需要去修改红黑树的平衡。下面是介绍如何去处理插入后的修护问题

  • case1:当前节点的父结点是红色,且当前 节点的祖父节点的另一个子节点(叔叔节点)也是红色
    处理策略:(1)将父结点设为黑色(2)将叔叔节点设置为黑色(3)将祖父节点设置为红色(4)将祖父节点设置为“当前节点”(红色节点):即,之后继续对当前节点进行操作。

  • case2:当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子
    处理策略:(1)将父结点作为新的当前节点(2)以新的当前节点为支点进行左旋转

  • case3:当前节点的父结点是红色,叔叔节点是黑色,且当前节点是其父结点的左孩子
    处理策略:(1)将父结点设置为黑色(2)将祖父节点设置为红色(3)以祖父节点为支点进行右旋转

再上面的代码中还有比较经典的函数,那就是左旋转和右旋转。因为那比较常用到。大家可以好好体会的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值