数据结构之平衡二叉树

1.重新平衡2子树,体现了归纳思想,以及简单的建模思想。

2.旋转名字不太好理解。自己觉得 “沿袭” 更恰当。自己为了进行沿袭这个动作。归纳了2个定理:

     1.父节点可垂直变为其左孩子或者左孩子的左孩子。左右同理。

     2.子树内部任意一支子树可代替原子树。

有这2个定理。就可以不用理解书上的旋转了,用自己的沿袭就可以完成单旋转和双旋转。毕竟大部分书,都只是告诉你如何旋转,还不如自己建模,抽象,定义定理。来实现。

3.重新计算树高度时,从变化的叶子开始要一直往父节点重新计算,所以很适合使用递归的方法,并保障从上往下时,是逐层往下,那么就可以保证插入和删除以后,会逐层检查子树高度,以及对比是否需要旋转

  而旋转后需要跟新子树的父节点。所以参考书上的方法是有返回节点的,一边递归回去的时候更新给父节点,而自己为了方便里面,没哟使用返回值,而是多加参数的方法,不简便,但好理解。

 

归纳和抽象建模思想体现在哪里。第一步到第二步的转变侧重体现了归纳,抽象,建模等基本思想。  第二步到第三步更体现了为了解决实际问题,修改模型的能力。val是一个进行抽象思维的练习的好例子,非常值得复习

 

 

 

 采用了2中不同的函数来完成 add .一个带返回值,一个不带返回值,而是使用参数。

package com.linson.datastrcture;





//自己的插入在递归中,并没有和书上返回节点。自己感觉自己无返回值的更好理解,代替方案就是放入一个头节点的父节点。好理解,但也繁琐点。
//递归返回根节点从代码的简洁上绝对更优,只是带参数的方法更容易理解。
//avl这个例子很值得复习。1.递归时的问题模型的确定  2.平衡树时的建模思维。 3.树高的递归计算,递归影响。这个很不错。4.compareble的系统库接口使用。5.左小右大思路的利用。
//6,删除时,需要平衡的时候会存在高的那一边的左右子树又一样高。比父节点的兄弟都高2级。

public class MyAVL<T extends Comparable<T>>
{
    public static class MyAVLNode<E extends Comparable<E>>
    {
        public E mElement;
        public MyAVLNode<E> mLeftNode;
        public MyAVLNode<E> mRightNode;
        public int mHeight;
        
        public MyAVLNode(E _value,MyAVLNode<E> left,MyAVLNode<E> right)
        {
            mElement=_value;
            mLeftNode=left;
            mRightNode=right;
            mHeight=1;
        }
        
        public int compareTo(E other)
        {
            return mElement.compareTo(other);
        }
    }
    
    public MyAVL()
    {
        mRootNode=null;
    }
    
    //问题:插入节点到树。组合:插入到节点,插入到左树,插入到右树. 基本值节点为空。可以插入。不需要再判断是否继续插入左或者右
    //节点高度默认是1,添加节点,必须逐层检测父节点:左右子树中最大值+1谁否大于现值? 大于要往上再检查,一直到某上层没变化。
    //所以返回值可以改为返回是否需要检查高度。但是想一下,又要增加返回值,又要判断是否需要检查,还不如每层都检查,反正AVL的话,数据再大也不会很高。1024才11层。
    public void add(T element,MyAVLNode<T> subTreeNode,MyAVLNode<T> fatherNode,boolean isLeft)
    {
        if(subTreeNode==null)
        {
            MyAVLNode<T> TempNode=new MyAVLNode<T>(element, null, null);//节点和树的泛型都实现了对比接口。所以树的参数,可以直接放入到节点中
            if(fatherNode!=null)
            {
                if(isLeft)
                {
                    fatherNode.mLeftNode=TempNode;
                }
                else 
                {
                    fatherNode.mRightNode=TempNode;
                }
            }
            else 
            {
                mRootNode=TempNode;
            }
        }
        else
        {
            boolean addIsBiger=subTreeNode.mElement.compareTo(element)<0;//泛型实现了对比接口
            if(addIsBiger)
            {
                add(element,subTreeNode.mRightNode,subTreeNode,false);
            }
            else 
            {
                add(element,subTreeNode.mLeftNode,subTreeNode,true);
            }
            RotationAndHeight(subTreeNode,fatherNode,isLeft);
        }
    }
    
    //问题:匹配一个树,组合:匹配根,匹配左树,匹配右树。基本问题:节点就是。
    //空:直接删,副节点设空。 有单子树。修改数据。左右树都有,找高度更高的换数据。 被换节点一定是叶子,删除叶子,父节点设空。
    //旋转和高度问题。要求
    //1.传过来的根节点参数的左右子树高度是正确的。那么RotationAndHeight函数就就可以往上递归正确的计算高度和进行修正。
    //2.remove函数往下递归时,保证是逐层进行的。那么才能保证RotationAndHeight会逐层往上。
    public void remove(T element,MyAVLNode<T> subTreeNode,MyAVLNode<T> fatherNode,boolean isLeft)
    {
        if(subTreeNode==null)
        {
            return;
        }
        int compareRet=subTreeNode.mElement.compareTo(element);
        if(compareRet==0)
        {
            if(subTreeNode.mLeftNode==null && subTreeNode.mRightNode==null)
            {
                if(fatherNode!=null)
                    if(isLeft)
                    {
                        fatherNode.mLeftNode=null;
                    }
                    else 
                    {
                        fatherNode.mRightNode=null;
                    }
                else 
                {
                    mRootNode=null;
                }
            }
            else if(subTreeNode.mLeftNode==null || subTreeNode.mRightNode==null)
            {
                if(fatherNode!=null)
                {
                    if(isLeft)
                    {
                        fatherNode.mLeftNode=subTreeNode.mLeftNode==null?subTreeNode.mRightNode:subTreeNode.mLeftNode;
                    }
                    else 
                    {
                        fatherNode.mRightNode=subTreeNode.mLeftNode==null?subTreeNode.mRightNode:subTreeNode.mLeftNode;
                    }
                }
                else 
                {
                    mRootNode=subTreeNode.mLeftNode==null?subTreeNode.mRightNode:subTreeNode.mLeftNode;
                }
            }
            else 
            {
                MyAVLNode<T> minnode= findMin(subTreeNode.mRightNode);
                subTreeNode.mElement=minnode.mElement;
                //可以确定是叶子,要效率高点。就可以新写个findMin,返回父节点。直接写代码删除。不需要这里一直递归。但对于修正平衡就没有办法往上递归了。
                remove(minnode.mElement, subTreeNode.mRightNode, subTreeNode, false);
                RotationAndHeight(subTreeNode,fatherNode,isLeft);
            }
        }
        else if(compareRet>0)//根节点更大。
        {
            remove(element, subTreeNode.mLeftNode,subTreeNode, true);
            RotationAndHeight(subTreeNode,fatherNode,isLeft);
        }
        else 
        {
            remove(element, subTreeNode.mRightNode,subTreeNode, false);
            RotationAndHeight(subTreeNode,fatherNode,isLeft);
        }
        
        
    }
    
    public MyAVLNode<T> findMax(MyAVLNode<T> subTreeNode)
    {

        MyAVLNode<T> tempRet=subTreeNode;
        while(tempRet!=null && tempRet.mRightNode!=null)
        {
            tempRet=tempRet.mRightNode;
        }
        return tempRet;
    }
    
    public MyAVLNode<T> findMin(MyAVLNode<T> subTreeNode)
    {

        MyAVLNode<T> tempRet=subTreeNode;
        while(tempRet!=null && tempRet.mLeftNode!=null)
        {
            tempRet=tempRet.mLeftNode;
        }
        return tempRet;
    }
    
    private void RotationAndHeight(MyAVLNode<T> subTreeNode,MyAVLNode<T> fatherNode,boolean isLeft)
    {
        //高度差=2 旋转,重新计算高度。
        //高度差<2. 根节点是否需要更新高度。
        //高度差>2 .错误。
        //断言对于快速开发和测试非常重要,而且减少正式版的编译代码和速度。不过如果有必要后期还是要写入到日志中.
        int leftHeight=subTreeNode.mLeftNode==null?0:subTreeNode.mLeftNode.mHeight;
        int rightHeight=subTreeNode.mRightNode==null?0:subTreeNode.mRightNode.mHeight;
        int maxSubTreeHeight=Math.max(leftHeight, rightHeight);
        
        assert(Math.abs(leftHeight-rightHeight)<=2) : "why .left compare to right is error";
        if(Math.abs(leftHeight-rightHeight)==2)
        {
            int ll=0,lr=0,rl=0,rr=0;
            int treetype=0;
            if(leftHeight-rightHeight==2)
            {
                assert(subTreeNode.mLeftNode!=null):"no way";
                ll=subTreeNode.mLeftNode.mLeftNode==null?0:subTreeNode.mLeftNode.mLeftNode.mHeight;
                lr=subTreeNode.mLeftNode.mRightNode==null?0:subTreeNode.mLeftNode.mRightNode.mHeight;
                if(ll>=lr)//这里用等号的话,删除的时候,可以用更简单的单转。
                {
                    MyAVLNode<T> newRoot= subTreeNode.mLeftNode;
                    subTreeNode.mLeftNode=newRoot.mRightNode;
                    newRoot.mRightNode=subTreeNode;
                    newRoot.mRightNode.mHeight=newRoot.mRightNode.mHeight-1;
                    if(isLeft && fatherNode!=null)
                    {
                        fatherNode.mLeftNode=newRoot;
                    }
                    else if (!isLeft && fatherNode!=null) {
                        fatherNode.mRightNode=newRoot;
                    }
                    else {
                        mRootNode=newRoot;
                    }
                }
                else 
                {
                    MyAVLNode<T> newRoot= subTreeNode.mLeftNode.mRightNode;
                    subTreeNode.mLeftNode.mRightNode=newRoot.mLeftNode;
                    newRoot.mLeftNode= subTreeNode.mLeftNode;
                    subTreeNode.mLeftNode=newRoot.mRightNode;
                    newRoot.mRightNode=subTreeNode;
                    newRoot.mHeight++;
                    newRoot.mLeftNode.mHeight--;
                    newRoot.mRightNode.mHeight--;
                    if(isLeft && fatherNode!=null)
                    {
                        fatherNode.mLeftNode=newRoot;
                    }
                    else if (!isLeft && fatherNode!=null) {
                        fatherNode.mRightNode=newRoot;
                    }
                    else {
                        mRootNode=newRoot;
                    }
                }
            }
            else 
            {
                assert(subTreeNode.mRightNode!=null):"no way";
                rl=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight;
                rr=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight;
                if(rr>=rl)//这里用等号的话,删除的时候,可以用更简单的单转。
                {
                    MyAVLNode<T> newRoot= subTreeNode.mRightNode;
                    subTreeNode.mRightNode=newRoot.mLeftNode;
                    newRoot.mLeftNode=subTreeNode;
                    newRoot.mLeftNode.mHeight=newRoot.mLeftNode.mHeight-1;
                    if(isLeft && fatherNode!=null)
                    {
                        fatherNode.mLeftNode=newRoot;
                    }
                    else if (!isLeft && fatherNode!=null) {
                        fatherNode.mRightNode=newRoot;
                    }
                    else {
                        mRootNode=newRoot;
                    }
                }
                else 
                {
                    MyAVLNode<T> newRoot= subTreeNode.mRightNode.mLeftNode;
                    subTreeNode.mRightNode.mLeftNode=newRoot.mRightNode;
                    newRoot.mRightNode= subTreeNode.mRightNode;
                    subTreeNode.mRightNode=newRoot.mLeftNode;
                    newRoot.mLeftNode=subTreeNode;
                    newRoot.mHeight++;
                    newRoot.mRightNode.mHeight--;
                    newRoot.mLeftNode.mHeight--;
                    if(isLeft && fatherNode!=null)
                    {
                        fatherNode.mLeftNode=newRoot;
                    }
                    else if (!isLeft && fatherNode!=null) {
                        fatherNode.mRightNode=newRoot;
                    }
                    else {
                        mRootNode=newRoot;
                    }
                }
            }
        }
        else if(Math.abs(leftHeight-rightHeight)==1)
        {
            
            assert((subTreeNode.mHeight-maxSubTreeHeight==0 || subTreeNode.mHeight-maxSubTreeHeight==1)) : "top node's height was wrong compare with left and right substree"; 
            if(subTreeNode.mHeight-maxSubTreeHeight==0)
            {
                subTreeNode.mHeight++;
            }
        }
    }
    
    
    
    
    //add:主要是4个问题。1,正确插入位置。2,更新新插入点的父节点数据。3.更新新插入点的父节点数据的高度,4.新插入点的父节点是否平衡
    //                   1,一般处理。2,采用递归+方法带返回值:新节点,那么当递归返回上层时,可给返回的新插入点赋予正确的父节点。
    //   3.4,保证递归方法是逐层进行,并保证新插入点的左右子树高度正确。那么递归回来,会保证新插入点及其所有父节点左右子树高度正确以及都检测旋转。
    public MyAVLNode<T> add(T element,MyAVLNode<T> addToThisNode)
    {
        MyAVLNode<T> ret=null;
        if(addToThisNode==null)//某条件下,成了基本问题
        {
            ret= new MyAVLNode<T>(element, null, null);
        }
        else//其他条件下,用更小规模问题来组合
        {
            int addIsBigger=element.compareTo(addToThisNode.mElement);
            if(addIsBigger<0)
            {
                addToThisNode.mLeftNode= add(element, addToThisNode.mLeftNode);
            }
            else 
            {
                addToThisNode.mRightNode=add(element, addToThisNode.mRightNode);
            }
            ret=addToThisNode;

        }
        //检查高度,检查是否需要旋转。
        ret=reHeightAndBalance(ret);
        if(addToThisNode==mRootNode)
        {
            mRootNode=ret;
        }
        return ret;
    }
    
    //remove 和add基本思路一样。稍微复杂一点.
    //有目标点有3种情况,1,无左右子树,那么删除叶子,2,左右一个为空。那么跳过要删除点,和左右子树想相连。
    //3.左右都不为空,本质和1是一样。删除右子树的最小值节点,也就是一个叶子。并把叶子的数据给目标点。
    //返回当前子树最高顶点。
    public MyAVLNode<T> remove(T element,MyAVLNode<T> removeThisNode)
    {
        MyAVLNode<T> ret=null;
        if(removeThisNode==null)
        {
            //空树或者没找到要删除的点,什么都不做,并返回null,因为本来就是null,返回给这个节点的父节点NULL,等于什么都没做。
            ret=null;
        }
        else 
        {
            int removeIsBigger=element.compareTo(removeThisNode.mElement);
            if(removeIsBigger==0)////某条件下,成了基本问题
            {
                if(removeThisNode.mLeftNode==null && removeThisNode.mRightNode==null)
                {
                    removeThisNode=null;//其实这句没用,栈内的一个变量赋值为空而已,也没达到手动释放的堆内存,而且是java了。没必要劳心内存问题。
                    ret=null;
                }
                else if(removeThisNode.mLeftNode==null || removeThisNode.mRightNode==null)//不可以写成 != || != .因为会包含 != && !=.而现在这样写,额外包含的,在上面已经被排除了.
                {
                    ret =removeThisNode.mLeftNode==null?removeThisNode.mRightNode:removeThisNode.mLeftNode;
                }
                else
                {
                    MyAVLNode<T> minNode= findMin(removeThisNode.mRightNode);
                    removeThisNode.mElement= minNode.mElement;
                    removeThisNode.mRightNode=remove(minNode.mElement, removeThisNode.mRightNode);
                    ret=removeThisNode;
                    //return remove(minNode.mElement, removeThisNode.mRightNode);//原写代码 严重错误。
                }
            }
            else if(removeIsBigger<0)
            {
                removeThisNode.mLeftNode= remove(element, removeThisNode.mLeftNode);
                ret=removeThisNode;
            }
            else 
            {
                removeThisNode.mRightNode= remove(element, removeThisNode.mRightNode);
                ret=removeThisNode;
            }
        }
        ret=reHeightAndBalance(ret);
        if(removeThisNode==mRootNode)
        {
            mRootNode=ret;
        }
        return ret;
    }
    
    
    //rotation: add .可从新加入点算起,add方法已经是逐步升入,那么会原路返回
    //remove:1. 双枝或空枝,逐步过来,最低点有null情况。返回nlll就好。 2.单枝情况,还好, 检查返回值的高度和平衡。再重新返回.
    private MyAVLNode<T> reHeightAndBalance(MyAVLNode<T> subTreeNode)
    {
        MyAVLNode<T> ret=subTreeNode;
        //null:返回 。 要平衡:平衡。算高度。
        if(subTreeNode==null)
        {
            ret=null;
        }
        else 
        {
            int leftHeight=subTreeNode.mLeftNode==null?0:subTreeNode.mLeftNode.mHeight;
            int rightHeight=subTreeNode.mRightNode==null?0:subTreeNode.mRightNode.mHeight;
            int maxSubTreeHeight=Math.max(leftHeight, rightHeight);
            
            assert(Math.abs(leftHeight-rightHeight)<=2) : "why .left compare to right is error";
            if(Math.abs(leftHeight-rightHeight)==2)
            {
                int ll=0,lr=0,rl=0,rr=0;
                int treetype=0;
                if(leftHeight-rightHeight==2)
                {
                    assert(subTreeNode.mLeftNode!=null):"no way";
                    ll=subTreeNode.mLeftNode.mLeftNode==null?0:subTreeNode.mLeftNode.mLeftNode.mHeight;
                    lr=subTreeNode.mLeftNode.mRightNode==null?0:subTreeNode.mLeftNode.mRightNode.mHeight;
                    if(ll>=lr)//这里用等号的话,删除的时候,可以用更简单的单转。
                    {
                        MyAVLNode<T> newRoot= subTreeNode.mLeftNode;
                        subTreeNode.mLeftNode=newRoot.mRightNode;
                        newRoot.mRightNode=subTreeNode;
                        newRoot.mRightNode.mHeight=newRoot.mRightNode.mHeight-1;
                        ret=newRoot;
                    }
                    else 
                    {
                        MyAVLNode<T> newRoot= subTreeNode.mLeftNode.mRightNode;
                        subTreeNode.mLeftNode.mRightNode=newRoot.mLeftNode;
                        newRoot.mLeftNode= subTreeNode.mLeftNode;
                        subTreeNode.mLeftNode=newRoot.mRightNode;
                        newRoot.mRightNode=subTreeNode;
                        newRoot.mHeight++;
                        newRoot.mLeftNode.mHeight--;
                        newRoot.mRightNode.mHeight--;
                        ret=newRoot;
                    }
                }
                else 
                {
                    assert(subTreeNode.mRightNode!=null):"no way";
                    rl=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight;
                    rr=subTreeNode.mRightNode.mLeftNode==null?0:subTreeNode.mRightNode.mLeftNode.mHeight;
                    if(rr>=rl)//这里用等号的话,删除的时候,可以用更简单的单转。
                    {
                        MyAVLNode<T> newRoot= subTreeNode.mRightNode;
                        subTreeNode.mRightNode=newRoot.mLeftNode;
                        newRoot.mLeftNode=subTreeNode;
                        newRoot.mLeftNode.mHeight=newRoot.mLeftNode.mHeight-1;
                        ret=newRoot;
                    }
                    else 
                    {
                        MyAVLNode<T> newRoot= subTreeNode.mRightNode.mLeftNode;
                        subTreeNode.mRightNode.mLeftNode=newRoot.mRightNode;
                        newRoot.mRightNode= subTreeNode.mRightNode;
                        subTreeNode.mRightNode=newRoot.mLeftNode;
                        newRoot.mLeftNode=subTreeNode;
                        newRoot.mHeight++;
                        newRoot.mRightNode.mHeight--;
                        newRoot.mLeftNode.mHeight--;
                        ret=newRoot;
                    }
                }
            }
            else if(Math.abs(leftHeight-rightHeight)==1)
            {
                assert((subTreeNode.mHeight-maxSubTreeHeight==0 || subTreeNode.mHeight-maxSubTreeHeight==1)) : "top node's height was wrong compare with left and right substree"; 
                if(subTreeNode.mHeight-maxSubTreeHeight==0)
                {
                    subTreeNode.mHeight++;
                }
                ret=subTreeNode;
            }
        }
        
        return ret;
    }

    //type:0:first print parent node .1 left children ,parent, right children. 2:left children ,right children parent.
    public void printTree(int type)
    {
        printTree(mRootNode, type,"");
    }
    
    //打印树:组合:打印左树,打印节点,打印右树。基本情况:叶子。
    private void printTree(MyAVLNode<T> node,int type,String space)
    {
        if(node==null)
        {
            System.out.println("");
            return;
        }
        
        String leafInfo=space+node.mElement.toString()+"["+node.mHeight+"]";
        String newSpace=space+"     ";
        
        if(node.mLeftNode==null && node.mRightNode==null)
        {
            System.out.println(leafInfo);
        }
        else
        {
            
            if(type==0)
            {
                System.out.println(leafInfo);
                if(node.mLeftNode!=null)
                {
                    printTree(node.mLeftNode, type,newSpace);
                }
                if(node.mRightNode!=null)
                {
                    printTree(node.mRightNode, type,newSpace);
                }
            }
            
            if(type==1)
            {
                
                if(node.mLeftNode!=null)
                {
                    printTree(node.mLeftNode, type,newSpace);
                }
                System.out.println(leafInfo);
                if(node.mRightNode!=null)
                {
                    printTree(node.mRightNode, type,newSpace);
                }
            }
            
            if(type==2)
            {
                if(node.mLeftNode!=null)
                {
                    printTree(node.mLeftNode, type,newSpace);
                }
                if(node.mRightNode!=null)
                {
                    printTree(node.mRightNode, type,newSpace);
                }
                System.out.println(leafInfo);
            }
            if(type==3)
            {
                if(node.mRightNode!=null)
                {
                    printTree(node.mRightNode, type,newSpace);
                }
                System.out.println(leafInfo);
                if(node.mLeftNode!=null)
                {
                    printTree(node.mLeftNode, type,newSpace);
                }
                
                
                
            }
        }
    }
    
    
    public MyAVLNode<T> mRootNode=null;
}

 

 

测试代码

public static class AVL
    {
        public static void test()
        {
            MyAVL<Integer> mytreeAvl=new MyAVL<Integer>();
//            mytreeAvl.add(20, mytreeAvl.mRootNode,null,true);
//            mytreeAvl.add(7, mytreeAvl.mRootNode,null,true);
//            //mytreeAvl.add(56, mytreeAvl.mRootNode,null,true);
//            //mytreeAvl.add(5, mytreeAvl.mRootNode,null,true);
//            //mytreeAvl.add(9, mytreeAvl.mRootNode,null,true);
//            //mytreeAvl.add(3, mytreeAvl.mRootNode,null,true);
////            mytreeAvl.add(12, mytreeAvl.mRootNode,null,true);
////            mytreeAvl.add(64, mytreeAvl.mRootNode,null,true);
////            mytreeAvl.add(33, mytreeAvl.mRootNode,null,true);
////            mytreeAvl.add(15, mytreeAvl.mRootNode,null,true);
            
            
            mytreeAvl.add(20, mytreeAvl.mRootNode);
            mytreeAvl.add(7, mytreeAvl.mRootNode);
            mytreeAvl.add(56, mytreeAvl.mRootNode);
            mytreeAvl.add(5, mytreeAvl.mRootNode);
            mytreeAvl.add(9, mytreeAvl.mRootNode);
            mytreeAvl.add(3, mytreeAvl.mRootNode);
            mytreeAvl.add(12, mytreeAvl.mRootNode);
//            mytreeAvl.add(64, mytreeAvl.mRootNode,null,true);
//            mytreeAvl.add(33, mytreeAvl.mRootNode,null,true);
//            mytreeAvl.add(15, mytreeAvl.mRootNode,null,true);
            
            
            //mytreeAvl.remove(20, mytreeAvl.mRootNode, null, true);
            //mytreeAvl.remove(5, mytreeAvl.mRootNode, null, true);
            
            mytreeAvl.printTree(3);
        }
    }

 

posted @ 2019-03-20 16:05  琴鸟  阅读(223)  评论(0编辑  收藏  举报