树结构合集一

1. AVL树

AVL树简单来说是带有平衡条件的二叉查找树.传统来说是其每个节点的左子树和右子树的高度最多差1(注意不能只保持根节点平衡).

1

如上图所示,只有左边的才是AVL树,右图在7这个节点处平衡被破坏.
我们都知道,删除和插入意味着有可能打破平衡,如上图,将6插入的话在8位置的平衡将被打破.这意味着我们需要做一定的修正,称为旋转.
对于失去平衡有四种姿态,LL(左左),LR(左右),RR(右右)和RL(右左).下面分别给出示意图:
2

除了上面的情况之外,还有其它的失去平衡的AVL树,如下图:
3

上图只是易于理解,归根结底只有四种姿态:LL,LR,RR,RL.
对于LL与RR我们可以采取单旋转解决,而对于LR与RL我们得采取稍显复杂的双旋转.

  • 单旋转

单旋转分为左左单旋转和右右单旋转

4
.
以上是左左单旋转的例子,比较简单不赘述
5

以上是右右单旋转的例子

  • 双旋转

6

首先上图解释了为什么单旋转无法解决这种状况,解决这个问题的左右双旋转如下图所示:
7

首先我们把子树Y看成是一个节点两个子树构成的,这并不影响啥.仔细观察我们会发现,整个过程实际上是先让k1节点做一次右右单旋转,再做一次k3节点的左左单旋转.
右左双旋转如下图所示,原理类似不再赘述
8

  • AVL实现

AVL树的实现我们用代码来模拟:

  package com.fredal.structure;
public class AVLTree<T extends Comparable<? super T>> {
   //内部节点类
   private static class AVLNode<T> {
       T element;
       AVLNode<T> left;//左孩子
       AVLNode<T> right;//右孩子
       int height;//当前高度

       AVLNode(T theElement) {
           this(theElement, null, null);
       }

       AVLNode(T theElement, AVLNode<T> lt, AVLNode<T> rt) {
           element = theElement;
           left = lt;
           right = rt;
           height = 0;
       }

   }
   
   private AVLNode<T> root;//根节点

   public AVLTree() {
       root=null;
   }
   //插入
   public void insert(T x){
       root=insert(x,root);
   }
   //删除
   public void remove(T x){
       root=remove(x,root);
   }
   
   //寻找最小值
   public T findMin(){
       if(isEmpty())
           throw new RuntimeException("空错误");
       return findMin(root).element;
   }
   
   //寻找最大值
   public T findMax(){
       if(isEmpty())
           throw new RuntimeException("空错误");
       return findMax(root).element;
   }
   //是否存在
   public boolean contains(T x){
       return contains(x,root);
   }
   //清空
   public void empty(){
       root=null;
   }
   //判空
   public boolean isEmpty(){
       return root==null;
   }
   //打印树
   public void printTree(){
       if(isEmpty())
           System.out.println("空树");
       else 
           printTree(root);
   }
   //判断是否平衡
   public void check(){
       check(root);
   }
   //判断是否平衡
   private int check(AVLNode<T> t) {
       if(t==null)
           return -1;
       if(t!=null){
           int hl=check(t.left);
           int hr=check(t.right);
           if(Math.abs(height(t.left)-height(t.right))>1||
                   height(t.left)!=hl||height(t.right)!=hr)
               System.out.println("出错了");
       }
       return height(t);
   }
   //获取高度
   private int height(AVLNode<T> t){
       return t==null?-1:t.height;
   }
   //中序遍历
   private void printTree(AVLNode<T> t) {
       if(t!=null){
           printTree(t.left);
           System.out.println(t.element);
           printTree(t.right);
       }
   }
   //是否包含 递归遍历
   private boolean contains(T x, AVLNode<T> t) {
       while(t!=null){
           int res=x.compareTo(t.element);
           if(res<0)
               t=t.left;
           else if(res>0)
               t=t.right;
           else 
               return true;//找到了
       }
       return false;
   }
   //寻找最小值
   private AVLNode<T> findMin(AVLNode<T> t) {
       if(t==null)
           return t;
       while(t.left!=null)
           t=t.left;
       return t;
   }
   //寻找最大值
   private AVLNode<T> findMax(AVLNode<T> t) {
       if(t==null)
           return t;
       while(t.right!=null)
           t=t.right;
       return t;
   }
   //插入操作
   private AVLNode<T> insert(T x, AVLNode<T> t) {
       if(t==null)
           return new AVLNode<T>(x, null, null);//插入当前的元素
       int res=x.compareTo(t.element);
       if(res<0)
           t.left=insert(x, t.left);
       else if(res>0)
           t.right=insert(x, t.right);
       else;//冲突了
       return balance(t);//确保平衡
   }
   //删除操作
   private AVLNode<T> remove(T x, AVLNode<T> t) {
       if( t == null )
           return t;   // 啥也不做         
       int res = x.compareTo( t.element );            
       if( res < 0 )
           t.left = remove( x, t.left );
       else if( res > 0 )
           t.right = remove( x, t.right );
       else if( t.left != null && t.right != null ) //两个 孩子
       {
           t.element = findMin( t.right ).element;//寻找右子树最小的
           t.right = remove( t.element, t.right );
       }
       else
           t = ( t.left != null ) ? t.left : t.right;//一个孩子 这种情况只有一层
       return balance( t );
   }
   //确保平衡状态
   private AVLNode<T> balance(AVLNode<T> t) {
       if(t==null)
           return t;
       if(height(t.left)-height(t.right)>1)//左子树比右子树深两层及以上
           if(height(t.left.left)>=height(t.left.right))
               t=singleRotateLL(t);//左左单旋转
           else
               t=doubleRotateLR(t);//左右双旋转
       else 
       if(height( t.right ) - height( t.left )>1)//右子树比左子树深两层以上
           if(height( t.right.right ) >= height( t.right.left ))//右右单旋转
               t=singleRotateRR(t);
           else 
               t=doubleRotateRL(t);//右左双旋转
       t.height=Math.max(height(t.left), height(t.right))+1;
       return t;
   }
   //右右单旋转
   private AVLNode<T> singleRotateRR(AVLNode<T> k1) {
       AVLNode<T> k2 = k1.right;
       k1.right=k2.left;
       k2.left=k1;
       k1.height = Math.max( height( k1.left ), height( k1.right ) ) + 1;
       k2.height = Math.max( height( k2.right ), k1.height ) + 1;
       return k2;
   }
   //左左单旋转
   private AVLNode<T> singleRotateLL(AVLNode<T> k2) {
       AVLNode<T> k1 = k2.left;
       k2.left = k1.right;
       k1.right = k2;
       k2.height = Math.max( height( k2.left ), height( k2.right ) ) + 1;
       k1.height = Math.max( height( k1.left ), k2.height ) + 1;
       return k1;
   }
   //右左双旋转
   private AVLNode<T> doubleRotateRL(AVLNode<T> k1) {
       //右子树先左左单旋转
       k1.right=singleRotateLL(k1.right);
       return singleRotateRR(k1);//再整个右右单旋转
   }
   //左右双旋转
   private AVLNode<T> doubleRotateLR(AVLNode<T> k3) {
       //左子树先右右单旋转
       k3.left=singleRotateRR(k3.left);
       return singleRotateLL(k3);//再整个左左单旋转
   }
   
   public static void main(String[] args) {
       AVLTree<Integer> tree=new AVLTree<Integer>();
       for(int i=1;i<=7;i++){
           tree.insert(i);
           tree.check();
       }
       tree.printTree();
       System.out.println("---------");
       for(int i=10;i<=16;i++){
           tree.insert(i);
           tree.check();
       }
       tree.insert(8);
       tree.insert(9);
       tree.printTree();
       System.out.println("----------");
       System.out.println(tree.findMin());
       System.out.println(tree.findMax());
       System.out.println(tree.root.height);
       System.out.println("--------------");
       System.out.println("没有输出说明成功了...");
       final int NUM=10000;
       for(int i=37;i!=0;i=(i+37)%NUM){//随机插入
           //System.out.println(i);
           tree.insert(i);
       }
       for(int i=1;i<NUM;i+=2){//删除所有奇数
           tree.remove(i);
       }
       for(int i=2;i<NUM;i+=2){
           if(!tree.contains(i))
               System.out.println("应该存在的偶数缺失");
       }
       for(int i=1;i<NUM;i+=2){
           if(tree.contains(i))
               System.out.println("不该存在的奇数存在");
       }
   }
}

可以看到测试类里我们使用了伪随机插入,这和线性同余稍有区别.足够大的随机数验证保证了AVL树的强壮.

2.伸展树

伸展树同样是一颗二叉查找树.对于AVL树而言,没插入一次就会呈现旋转,影响了插入和删除的效率,此时一个新的变种伸展树就被提出了.
它的优秀性能基于以下情况,如果我们每次都查询相同的节点或者频繁查询该节点,AVL树在节点越来越多的情况下呈现下旋,复杂度只会递增.而伸展树的想法就是,查询一个节点的时候就通过一些列类似AVL旋转把它顶到根节点.这样下次如果还是访问该节点,就特别方便.
基本上我们考虑以下两种状况,之字形一字型.假设我们要查找X节点,那么:

9

以上是之字形旋转,可以看到,类似于AVL的双旋转.
还有一字型如下所示:
10

这种情况像是加强版的单旋转,需要做两次单旋转.
当然还有一种状况,就是纯粹的单旋转,比如X节点就是根节点的孩子,那么只要单旋转一次就行了.
接下来将自顶向下伸展树,这真是一种极好的实现方式,使得之字形与单旋转几乎不用工作,而一字型只需要一次单旋转即可,剩余的工作交给分离与合并即可.
11

上图即是三种情况分别对应的操作(单旋转查找Y,一字型查找Z,之字形查找Z),这种伸展方式把树分为L树,M树与R树,然后自顶向下查找元素,仅仅在一字型这种状况时候需要先进行单旋转操作预处理.比较目标与根节点的值,目标小就把根节点分离到R树,目标更大就把根节点分离到L树...一直到寻找到目标或者寻找到离目标最近的那个数.
分离到最后一层后,再进行合并操作即可:
12

上面即是自顶向下伸展树的伸展方法(查找)方法的全过程.我们得到的是刚好是目标,或者目标的前驱或后继.那么我们在执行插入操作的时候.仅仅是考虑插入数与根节点的大小关系插入即可,因为根节点的所有左子树必然小于插入数,根节点的右子树必然大于插入数.不用担心子树层及后面...
删除的逻辑同样很简单,不再赘述.

  package com.fredal.structure; public class SplayTree<T extends Comparable<? super T>> {
    //内部节点类
    private static class BinaryNode<T>{
        T element;
        BinaryNode<T> left;//左子树
        BinaryNode<T> right;//右子树
        public BinaryNode(T element) {
            this(element, null, null);
        }
        public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) {
            this.element = element;
            this.left = left;
            this.right = right;
        }    
    }  
    private BinaryNode<T> root;
    private BinaryNode<T> nullNode;//表示null引用
    private BinaryNode<T> newNode=null;//用于各种插入
    private BinaryNode<T> header=new BinaryNode<T>(null);//用于展开 作为头节点

    public SplayTree(){
        nullNode=new BinaryNode<T>(null);
        nullNode.left=nullNode.right=nullNode;
        root=nullNode;
    }   
    //插入操作 进行展开操作后 只要比较和根节点的大小进行一次操作即可
    public void insert(T x){
        if(newNode==null)
            newNode=new BinaryNode<T>(null);
        newNode.element=x;
        if(root==nullNode){
            newNode.left=newNode.right=nullNode;
            root=newNode;
        }
        else{
            root=splay(x,root);//伸展操作 将目标或者离目标最近的那个顶到根部
            int res=x.compareTo(root.element);
            if(res<0){
                newNode.left=root.left;
                newNode.right=root;
                root.left=nullNode;
                root=newNode;
            }else if(res>0){
                newNode.right=root.right;
                newNode.left=root;
                root.right=nullNode;
                root=newNode;
            }else
                return;//重复了
        }
        newNode=null;
    }
    //删除操作
    public void remove(T x){
        if(!contains(x))//不包含 就返回
            return;
        BinaryNode<T> newTree;
        root=splay(x, root);
        if(root.left==nullNode)
            newTree=root.right;//左边为空 直接右子树接上 
        else{
            newTree=root.left;
            newTree=splay(x, newTree);//找到左子树最大的作为根
            newTree.right=root.right;
        }
        root=newTree;
    }
    //寻找最大值
    public T findMax(){
        if(isEmpty())
            throw new RuntimeException("空了");
        BinaryNode<T> t=root;
        while(t.right!=nullNode)
            t=t.right;//找到最大值
        root=splay(t.element, root);//并伸展到根
        return t.element;
    }
    //寻找最小值
    public T findMin(){
        if(isEmpty())
            throw new RuntimeException("空了");
        BinaryNode<T> t=root;
        while(t.left!=nullNode)
            t=t.left;//找到最小值
        root=splay(t.element, root);//并伸展到根
        return t.element;
    }
    //是否包含
    public boolean contains(T x){
        if(isEmpty())
            return false;
        root=splay(x, root);
        return root.element.compareTo(x)==0;
    }
    //清空
    public void empty(){
        root=nullNode;
    }
    //判空
    public boolean isEmpty(){
        return root==nullNode;
    }
    //内部方法展开 找到一个刚好等于或者刚好大于或者刚好小于目标的数
    private BinaryNode<T> splay(T x, BinaryNode<T> t) {
        BinaryNode<T> leftTree,rightTree;//L R树
        header.left=header.right=nullNode;
        leftTree=rightTree=header;
        nullNode.element=x;//保证匹配
        for(;;){
            int res=x.compareTo(t.element);
            if(res<0){
                //"一字型"旋转
                if(x.compareTo(t.left.element)<0)
                    t=rotateL(t);
                if(t.left==nullNode)
                    break;
                //分离到R树 同时备份在header中
                rightTree.left=t;
                rightTree=t;
                t=t.left;               
            }else if(res>0){
                //"一字型旋转"
                if(x.compareTo(t.right.element)>0)
                    t=rotateR(t);
                if(t.right==nullNode)
                    break;
                //分离到L树 同时备份在header中
                leftTree.right=t;
                leftTree=t;
                t=t.right;
            }else
                break;
        }
        //最后一次分裂
        leftTree.right=t.left;
        rightTree.left=t.right;
        //合并操作
        //注意header.right相当于leftTree.right
        t.left = header.right;
        //header.left相当于righttree.left
        t.right = header.left;
        return t;
    }
    //右右单旋转
    private BinaryNode<T> rotateR(BinaryNode<T> k1) {
        BinaryNode<T> k2 = k1.right;
        k1.right=k2.left;
        k2.left=k1;
        return k2;
    }
    //左左单旋转
    private BinaryNode<T> rotateL(BinaryNode<T> k2) {
        BinaryNode<T> k1 = k2.left;
        k2.left=k1.right;
        k1.right=k2;
        return k1;
    }    
    public static void main(String[] args) {
        SplayTree<Integer> tree=new SplayTree<Integer>();
        final int NUM=40000;
        System.out.println("如果程序不输出就成功了....");
        for(int i=37;i!=0;i=(i+37)%NUM)
            tree.insert(i);//伪随机
        for(int i=1;i<NUM;i+=2)
            tree.remove(i);//删除所有奇数
        if( tree.findMin( ) != 2 || tree.findMax( ) != NUM - 2 )
            System.out.println( "最大值最小值寻找错误!" );
        for( int i = 2; i < NUM; i += 2 )
            if( !tree.contains( i ) )
                System.out.println( "偶数缺失" + i );
        for( int i = 1; i < NUM; i += 2 )
            if( tree.contains( i ) )
                System.out.println( "奇数多余" + i );
    }
}

总结伸展树就是一个"顶"字.

3.红黑树

红黑树不愧是最难的数据结构之一...
R-B Tree,红黑树,同样是一颗特殊的二叉查找树变种.
红黑树拥有四大性质:

  1. 每个节点或者是红色,或者是黑色.
  2. 根是黑色的
  3. 如果一个节点是红色的,那么其子节点必须是黑色的.
  4. 从一个节点到一个null引用(即逻辑末尾)的每一条路径必须包含相同数目的黑色节点.
    我们用双圈表示红色,一个例子如下:
    13

    红黑树的复杂度为(O log(N)),效率十分高,应用十分广泛,如java中的TreeSet,TreeMap,Linux虚拟内存管理.
  • 复杂度相关证明

首先我们将会证明一颗含有n个节点的红黑树高度至多为2log(n+1).这个命题将保证查找操作是一种对数的操作,复杂度为O(log n).
然后上述命题的逆否命题为"高度为h的红黑树,它的包含的内节点个数至少为 2h/2-1个".我们只需证明这个就行
从某个节点x出发(不包括该节点)到达一个叶节点的任意一条路径上,黑色节点的个数称为该节点的黑高度(x's black height),记为bh(x).关于bh(x),我们可以得到两点:
a. 根据特性4,即从一个节点到一个null引用(即逻辑末尾)的每一条路径必须包含相同数目的黑色节点.得知bh(x)是唯一的.
b. 根据特性3.即如果一个节点是红色的,那么其子节点必须是黑色的.得知bh(x)≥h/2.进而得到证明"高度为h的红黑树,它包含的黑节点个数至少为2bh(x)-1个"即可.
接下来通过"数学归纳法"证明上述结论.

  1. 树的高度为0,bh(x)为0,2bh(x)-1为0.命题成立.
  2. 假设,当树高度为h-1,它包含的个数至少为2bh(x)-1-1.成立.
    当树的高度为h的时候,根节点x的左右子树,高度为h-1,黑高度为bh(x)或bh(x)-1.
    则节点x所包含的节点至少是 ( 2bh(x)-1-1 ) + ( 2bh(x)-1-1 )+1 = 2bh(x)-1.原命题成立.
  • 插入

首先我们讲旋转时候都用avl树的术语,和红黑树的旋转正好相反,讲的各种情况的镜像也不再赘述...
这里是分为自底向上插入和自顶向下插入.首先默认n为当前点,p为父节点,g为祖父节点,u为叔叔节点,t为兄弟节点,先讲自底向上的插入:

  1. 如果插入的是根节点,那么直接把此节点涂黑
  2. 插入的节点的父亲是黑色的,啥也不做.
  3. n的叔叔即u是红色的,这时候需要递归,我们在g上进行递归


    14
  4. u是黑色的,n是右孩子,对p进行右右单旋转变到状态5


    15
  5. u是黑色的,n是左孩子,对g点左左单旋转


    16

情况3使得自底向上插入需要增加父亲指针,结构体变大,编程特麻烦.
所以我们考虑用自顶向下插入实现,一个思路就是,使得u永远是黑色的,就是避免了情况3的出现.
首先一种情况是碰到当前节点有两个红儿子,那么就需要翻转两个孩子和当前节点的颜色即可.如下:

17

这种情况只有在n的父节点也是红色的时候才会冲突,同时我们可以保证叔叔节点u一定是黑色的.那么自然而然可以参考上面的情况4与5解决问题.

  • 删除

同样有自底向上和自顶向下两种,算法导论以及大部分的博客都写的自底向上的删除.这种方法要分好多种情况,而且比较难理解,我自己看得云里雾里.具体可以参考(http://blog.csdn.net/v_JULY_v/article/details/6105630) ,有六篇...
我还是来讲讲自顶向下删除,相对容易理解而且编码简单,虽然也蛮复杂.
首先可以分为两个大类A和B,然后我们按步骤step区分顺序.实在不想画图,网上找了图,白色代表红色,黑色代表黑色.x为当前节点,p为父节点,t为兄弟节点
Step1:
Step1A:表示x的两个孩子都是黑色的,下面分为三种情况
Step1A1:T有两个黑色的孩子,p,x,t节点变色

18

Step1A2:T有一个红色的左孩子,x,p变色,右左双旋转:
19

Step1A3:T有一个红色的右孩子,或者两个都是红色(这个放在上面也行的),这个..一字型旋转.x,p,t,r都变色
20

现在我们得到了红色的x节点,开始判断:
Step1_2A:
Step1_2A1:不是要删除的节点:继续下降,回到Step1初始阶段
Step1_2A2:是要删除的节点:判断是不是叶子节点.如果是就直接删除.不是的话,就要用右子树上的最小值或者左子树上的最大值代替x节点中的值.同时往右下或者左下降,回到Step1...
接下来是B情况了
Step1B:x至少有一个红色的孩子,这里我们先判断x是否是要找的值
Step1_2B1:x是要找的值:除了不用判断是不是叶子节点外,和Step1_2A2相同...
Step1_2B2:x不是:那么继续下降,分为两种情况,落在红孩子或者黑孩子上
Step1_2B2_1:落在红色的节点上,回到Step1_2A.
Step1_2B2_2:落在黑色的节点上
21

22

把上面那幅图的情况变成下面那副图的情况,p,t变色,p右右单旋转.然后转到了Step1A,或者Step1B.
我们以及完成了把当前节点变成红色,并且达到叶子节点删除的过程.
Step2:把根节点染成黑色

  • 自顶向下红黑树的实现

删除和插入都是采用自顶向下的,其实编码有非常多要注意的地方.

  package com.fredal.structure;
import javax.print.attribute.standard.Finishings;
import javax.swing.border.Border;
public class RedBlackTree<T extends Comparable<? super T>> {
   // 内部节点类
   private static class RedBlackNode<T> {
       T element;
       RedBlackNode<T> left;
       RedBlackNode<T> right;
       int color;// 颜色

       public RedBlackNode(T element) {
           this(element, null, null);
       }

       public RedBlackNode(T element, RedBlackNode<T> left,
               RedBlackNode<T> right) {
           this.element = element;
           this.left = left;
           this.right = right;
           color = RedBlackTree.BLACK;
       }

   }

   private static RedBlackNode header;// 头结点
   private static RedBlackNode nullNode;// 逻辑空节点
   private static final int BLACK = 1;
   private static final int RED = 0;

   private static RedBlackNode current;
   private static RedBlackNode parent;
   private static RedBlackNode grand;
   private static RedBlackNode great;// 曾祖父
   private static RedBlackNode brother;// 兄弟节点

   public RedBlackTree() {
       nullNode = new RedBlackNode<T>(null);
       nullNode.left = nullNode.right = nullNode;
       header = new RedBlackNode<T>(null);
       header.left = header.right = nullNode;// 初始化
   }
      
   // 自顶向下插入
   public void insert(T item) {
       current = parent = grand = header;// 初始化
       nullNode.element = item;

       while (compare(item, current) != 0) {
           great = grand;
           grand = parent;
           parent = current;
           current = compare(item, current) < 0 ? current.left : current.right;

           // 如果有两个红孩子 进行处理
           if (current.left.color == RED && current.right.color == RED)
               handleReorient(item);
       }

       if (current != nullNode)// 重复了
           return;
       current = new RedBlackNode<T>(item, nullNode, nullNode);// 插入节点
       if (compare(item, parent) < 0)
           parent.left = current;
       else
           parent.right = current;
       handleReorient(item);// 处理一下叶子 左右两个null引用染黑
   }

   // 处理操作
   private void handleReorient(T item) {
       // 染色
       current.color = RED;
       current.left.color = BLACK;
       current.right.color = BLACK;

       if (parent.color == RED) {// 只有这种情况需要翻转
           grand.color = RED;
           if ((compare(item, grand) < 0) != (compare(item, parent) < 0))// 之字形
               rotate(item, grand);// 先变成一字型
           current = rotate(item, great);
           current.color = BLACK;
       }
       header.right.color = BLACK;// 根节点染黑
   }

   private RedBlackNode<T> rotate(T item, RedBlackNode<T> parent) {
       if (compare(item, parent) < 0)
           // 返回旋转后的根节点
           return parent.left = compare(item, parent.left) < 0 ? 
                   rotateL(parent.left): // LL旋转
                   rotateR(parent.left);// LR旋转
       else
           return parent.right = compare(item, parent.right) < 0 ? 
                   rotateL(parent.right): // RL旋转
                   rotateR(parent.right);// RR旋转
   }

   // 右右单旋转
   private static RedBlackNode rotateR(RedBlackNode k1) {
       RedBlackNode k2 = k1.right;
       k1.right = k2.left;
       k2.left = k1;
       return k2;
   }

   // 左左单旋转
   private static RedBlackNode rotateL(RedBlackNode k2) {
       RedBlackNode k1 = k2.left;
       k2.left = k1.right;
       k1.right = k2;
       return k1;
   }

   private int compare(T item, RedBlackNode<T> t) {
       if (t == header)// 由于我们把header.right作为根
           return 1;
       else
           return item.compareTo(t.element);
   }

   // 清空
   public void makeEmpty() {
       header.right = nullNode;
   }

   // 判空
   public boolean isEmpty() {
       return header.right == nullNode;
   }

   // 寻找最大值
   public T findMax() {
       if (isEmpty())
           throw new RuntimeException("这是空树");
       RedBlackNode<T> itr = header.right;
       while (itr.right != nullNode)
           itr = itr.right;
       return itr.element;
   }

   // 寻找最小值
   public T findMin() {
       if (isEmpty())
           throw new RuntimeException("这是空树");
       RedBlackNode<T> itr = header.right;
       while (itr.left != nullNode)
           itr = itr.left;
       return itr.element;
   }

   // 寻找值
   public RedBlackNode<T> find(T x, RedBlackNode<T> t) {
       RedBlackNode<T> parent = nullNode;
       while (t != nullNode && t.element != x) {
           parent = t;
           if (x.compareTo(t.element) < 0)
               t = t.left;
           else
               t = t.right;
       }
       if (t == nullNode)
           return parent;
       else
           return t;
   }

   // 是否包含
   public boolean contains(T x) {
       nullNode.element = x;
       current = header.right;
       for (;;) {
           if (x.compareTo((T) current.element) < 0)
               current = current.left;
           else if (x.compareTo((T) current.element) > 0)
               current = current.right;
           else if (current != nullNode)
               return true;
           else
               return false;
       }
   }

   // 打印树
   public void printTree() {
       if (isEmpty())
           System.out.println("这是课空树");
       else
           printTree(header.right,0);
   }

   // 中序遍历
   private void printTree(RedBlackNode<T> t,int depth) {
       if (t != nullNode) {
           printTree(t.left,depth+1);
           for(int i=0;i<depth;i++)
               System.out.print("  ");
           System.out.println(t.element + ":"
                   + (t.color == 1 ? "black" : "red"));
           printTree(t.right,depth+1);
       }
   }

   // 删除节点
   public void remove(T item) {
       header.color = RED;
       current = header.right;
       brother = header.left;
       grand = great = parent = header;

       while (current != nullNode) {
           // 1A
            if (current.left.color == BLACK && current.right.color == BLACK) {
               // 1A1
                if (brother.left.color == BLACK && brother.right.color == BLACK) {
                   parent.color = BLACK;
                   current.color = RED;
                   if(brother!=nullNode)
                    brother.color = RED;
               }
               // 要考虑镜像
               else {
                   solve1A23();
               }
               // 完成染色current为红色 父节点为黑色 开始判断
               if (current.element == item)
                   item = findItem(item);// 完成前进或者完成删除
               else
                   normalDown(item);// 继续下降
           } else {// 1B
                   // 先进行判断
               if (current.element != item)
                   normalDown(item);// 下降
               else
                   item = findItem(item);
               if (current == nullNode)
                   break;
               // 已经完成下降
               if (current.color == BLACK) {// 下降到黑色
                   solve1B();
               } else if (current.element != item)// 下降到红色 不是要找的 继续下降
                   normalDown(item);
               else
                   item = findItem(item);
           }
       }
       // 2
       header.color = BLACK;
       header.right.color = BLACK;// 复原
   }
   //解决1A2和1A3
   private static void solve1A23(){
       if (parent.left == current) {// 左孩子
           if (brother.left.color == RED) {// 1A2
               current.color = RED;
               parent.color = BLACK;
               parent.right = rotateL(brother);
               if (grand.right == parent)
                   grand.right = rotateR(parent);
               else
                   grand.left = rotateR(parent);
           } else {// 1A3
               current.color = RED;
               parent.color = BLACK;
               brother.color = RED;
               brother.right.color = BLACK;

               if (grand.right == parent)
                   grand.right = rotateR(parent);
               else
                   grand.left = rotateR(parent);
           }
       } else {// 右孩子 镜像操作
           if (brother.right.color == RED) {// 1A2
               current.color = RED;
               parent.color = BLACK;
               parent.left = rotateR(brother);
               if (grand.left == parent)
                   grand.left = rotateL(parent);
               else
                   grand.right = rotateL(parent);
           } else {// 1A3
               current.color = RED;
               parent.color = BLACK;
               brother.color = RED;
               brother.left.color = BLACK;

               if (grand.right == parent)
                   grand.right = rotateL(parent);
               else
                   grand.left = rotateL(parent);
           }
       }
   }
   //解决1B情况
   private static void solve1B(){
       brother.color = BLACK;
       parent.color = RED;
       if (parent.left == current) {
           if (grand.left == parent)
               grand.left = rotateR(parent);
           else
               grand.right = rotateR(parent);
           brother = parent.right;
       } else {// 镜像
           if (grand.left == parent)
               grand.left = rotateL(parent);
           else
               grand.right = rotateL(parent);
           brother = parent.left;
       }
   }
   // 下降
   private void normalDown(T item) {
       if (compare(item, current) < 0) {
           grand = parent;
           parent = current;
           brother = parent.right;
           current = current.left;
       } else {
           grand = parent;
           parent = current;
           brother = parent.left;
           current = current.right;
       }

   }

   private T findItem(T item) {
       T temp;
       RedBlackNode<T> toDelete;
       // 先判断是否是叶子节点
       if (current.left == nullNode && current.right == nullNode) {
           if (parent.right == current)// 左孩子
               parent.right = nullNode;
           else
               parent.left = nullNode;
           current = nullNode;
           temp = item;
       } else {// 不是叶子节点
           if (current.right != nullNode) {// 右子树找一个最小的
               toDelete = find(item, current.right);
               current.element = toDelete.element;
               temp = toDelete.element;
               if (toDelete.color == RED) {// 找到的是红色节点
                   current.right = deleteNode(toDelete, current.right);
                   current = nullNode;
               } else {
                   grand = parent;
                   parent = current;
                   brother = parent.left;
                   current = current.right;//下降循环直到叶子
               }
           } else {// 从左子树找一个最大的
               toDelete = find(item, current.left);
               current.element = toDelete.element;
               temp = toDelete.element;
               if (toDelete.color == RED) {// 找到的是红色节点
                   current.left = deleteNode(toDelete, current.left);
                   current = nullNode;
               } else {
                   grand = parent;
                   parent = current;
                   brother = parent.right;
                   current = current.left;//下降循环直到叶子
               }
           }
       }
       return temp;
   }

   // 删除节点
   private RedBlackNode<T> deleteNode(RedBlackNode<T> target, RedBlackNode<T> t) {
       RedBlackNode<T> origin = t;
       RedBlackNode<T> parent = nullNode;
       while (t != target) {
           parent = t;
           if (target.element.compareTo(t.element) < 0)
               t = t.left;
           else
               t = t.right;
       }
       if (t == origin) {
           RedBlackNode<T> temp;
           if (t.right != nullNode)
               temp = t.right;
           else
               temp = t.left;
           return temp;
       }
       if (parent.right == t) {
           if (t.right != nullNode)
               parent.right = t.right;
           else
               parent.right = t.left;
       } else {
           if (t.right != nullNode)
               parent.left = t.right;
           else
               parent.left = t.left;
       }
       return origin;
   }

   public static void main(String[] args) {
       RedBlackTree<Integer> t = new RedBlackTree<Integer>();
       t.insert(10);
       t.insert(85);
       t.insert(15);
       t.insert(70);
       t.insert(20);
       t.insert(60);
       t.insert(30);
       t.insert(50);
       t.insert(65);
       t.insert(80);
       t.insert(90);
       t.insert(40);
       t.insert(5);
       t.insert(55);
       t.insert(45);
       t.printTree();
       System.out.println();
       t.remove(30);
       t.printTree();
       System.out.println();
       t.remove(40);
       t.printTree();
        System.out.println();
        t.remove(5);
        t.printTree();
        System.out.println();
        t.remove(70);
        t.printTree();
        System.out.println();
        t.remove(90);
        t.printTree();
        System.out.println();
        t.remove(85);
        t.printTree();
        System.out.println();
        t.remove(20);
        t.printTree();
        System.out.println();
        t.remove(45);
        t.printTree();
        System.out.println();
        t.remove(50);
        t.printTree();
        System.out.println();
        t.remove(55);
        t.printTree();
        System.out.println();
        t.remove(60);
        t.printTree();
        System.out.println();
        System.out.println();
        t.remove(80);
        t.printTree();
        System.out.println();
        t.remove(10);
        t.printTree();
        System.out.println();
        t.remove(15);
        t.printTree();
        System.out.println();
   }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,723评论 0 33
  • 红黑树是平衡二叉查找树的一种。为了深入理解红黑树,我们需要从二叉查找树开始讲起。 BST 二叉查找树(Binary...
    kanehe阅读 1,370评论 0 8
  • 红黑树为一棵二叉搜索树,它为每个结点增加一个变量存储结点颜色,利用结点颜色对树的形状进行约束,使其近似平衡(并非完...
    LRC_cheng阅读 476评论 0 1
  • 成都的秋和夏总是缠缠绵绵,直到十一月,也不尽然是完全的秋,偶尔还要来个二十多度,给人一种快要夏天的错觉。街道上,...
    yi梓君阅读 220评论 0 0
  • 最近睡眠一般吧,因为老做梦,早晨起来很累,像没有睡一般,这就是浅睡眠吧同时,最近做了很多奇怪的梦1、去知乎一对情侣...
    田田kyle阅读 149评论 0 0