程序员数据结构和算法分析

树结构合集一

2016-03-03  本文已影响806人  fredal

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树的实现我们用代码来模拟:

  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();
   }
}

上一篇 下一篇

猜你喜欢

热点阅读