数据结构——从0到1构建平衡二叉搜索树


前言

此博客仅是本人复习时所记录

文章较长,但内容全面。涉及到了平衡二叉搜索树的插入和删除讲解,以及代码实现。

最后附上源码,本人调试无问题,但不能保证完全不出错。

欢迎大家,指出错误。

平衡二叉搜索树

平衡二叉搜索树(Balanced binary search tree),顾名思义,兼顾了 平衡二叉树 的性质与 二叉搜索树 的性质。

这里简单提及一下两种树形结构的性质。

二叉搜索树

它或者是一棵空树,或者是具有下列性质的二叉树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树。

作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势

在这里插入图片描述

平衡二叉树

root根节点的左、右子树的高度(H)的差作为,root根的平衡因子(使用BF表示),树中每个节点的平衡因子绝对值不大于 1,则为平衡二叉树。

左子树和右子树均是平衡二叉树。

注意:树的高度 = max(左子树的高度 , 右子树的高度)+1

如图是平衡二叉树
在这里插入图片描述

如下图 失衡二叉树

B->H=3 C->H=1 则 A->BF=abs(3-1)=2 则此时A是失去平衡的。

就此图而言,使得A变得失衡得原因是由于B->H 比 C->H 大了不止1个深度。

容易看出,A之所以失衡正是因为树中插入了新节点 F。F未出现之前,整个树的所有节点都保持着平衡。

如何解决这个问题,使得平衡重新出现?

在这里插入图片描述

四种失衡情况

这里列举出,因为INSERT插入节点,破坏了原有平衡的四种情况以及每种情况的调整方案。

注意:newNode的插入导致了A节点的失衡

LL:newNode插入在了A的左儿子的左子树中。
LR: newNode插入在了A的左儿子的右子树中。
RR: newNode插入在了A的右儿子的右子树中。
RL: newNode插入在了A的右儿子的左子树中。

调整原则是 降低子树高度 以及 保持原有树的遍历顺序
方法则是进行 平衡旋转

  • LL

A节点由于新插入节点newNode,从而变得失衡。
此时 B->H=1+h+1 , A->BF=abs( (1+h+1) - h )=2 【其中,h可以为0,也可以为任意数,代表了隐藏子树的高度
在这里插入图片描述
由于新插入的节点newNode,是插入在了失衡的节点A的左儿子的左子树中。
所以这种情况,叫做LL

调整依据: 先根据最小的子问题来理解,假设h=0

中序遍历顺序为 C-B-A ,所以将B作为根节点,既保持了遍历顺序,又解决了失衡问题。
此时 对失衡节点进行右旋(顺时针)

即使失衡节点的左儿子为根
在这里插入图片描述

看看这个树的中序遍历顺序 newNode-α-B-β-A-γ

调整方案:

  1. 将B节点作为新的根节点
  2. B的左子树α不变
  3. B的右儿子变为A节点
  4. B原来的右子树β成为A节点的左子树【为了满足中序遍历顺序不变】

在这里插入图片描述

代码实现:

	/**
     * LL情况  进行右旋
     *
     * @param node 出现失衡的根节点
     * @return  右旋后的新根节点
     */
    private BSTNode<T> leftLeftRotation(BSTNode<T> node)
    {
    	//curNode作为新的root
        BSTNode<T> curRoot = node.leftChild;
        //curNode交接自己的右儿子
        node.leftChild=curRoot.rightChild;
        //拥有新的右儿子
        curRoot.rightChild=node;
        return curRoot;
    }
  • LR

A节点由于新插入节点newNode,从而变得失衡。
此时 B->H=max(h+1 , h+2)+1=h+3 , A->BF=abs( (h+3)-(h+1) )=2 【其中,h可以为0,也可以为任意数,代表了隐藏子树的高度
在这里插入图片描述
由于新插入的节点newNode,是插入在了失衡的节点A的左儿子的右子树中。
所以这种情况,叫做LR

调整依据:根据最小的子问题来理解,假设(h+1)=0

其中由于C的插入,A失衡。

中序遍历顺序为 B-C-A ,所以将C作为根节点,既保持了遍历顺序,又解决了失衡问题。
此时 对失衡节点的左儿子左旋 在对失衡节点右旋
在这里插入图片描述
看看这个树的中序遍历顺序 α-B-newNode-β-C-γ-A-δ

调整方案:

  1. 将C节点作为新的根节点
  2. C的左儿子变为B节点
  3. C的右儿子变为A节点
  4. C原来的左子树β成为B节点的右子树
  5. C原来的右子树γ成为A节点的左子树【为了满足中序遍历顺序不变】

在这里插入图片描述

注意:切不可理解为是将新添加节点作为根,由于新添加节点可能与失衡节点,距离了一大片树区域,这个是不行的。我们的原则是使得失衡节点的左儿子的右儿子为根。

为了方便理解每一步,手动作图

在这里插入图片描述

代码如下:

	
	/**
     * LR情况  先对根节点的左儿子左旋  再对根节点右旋
     * @param node 出现失衡的根节点
     * @return  左旋+右旋后的新的根节点
     */
    private BSTNode<T> leftRightRotation(BSTNode<T> node)
    {
        node.leftChild=rightRightRotation(node.leftChild);
        node=leftLeftRotation(node);
        return node;
    }
  • RR

A节点由于新插入节点newNode,从而变得失衡。
此时 B->H=max(h , h+1)+1=h+2 , A->BF=abs( h-(h+2) )=2 【其中,h可以为0,也可以为任意数,代表了隐藏子树的高度
在这里插入图片描述
由于新插入的节点newNode,是插入在了失衡的节点A的右儿子的右子树中。
所以这种情况,叫做RR

调整依据: 先根据最小的子问题来理解,假设h=0

在这里插入图片描述

中序遍历顺序为 A-B-C ,所以将B作为根节点,既保持了遍历顺序,又解决了失衡问题。
此时 对失衡节点进行左旋(逆时针)

即使失衡节点的右儿子为根

调整方案:

  1. 将B节点作为新的根节点
  2. B的右子树β不变
  3. B的左儿子变为A节点
  4. B原来的左子树α成为A节点的右子树【为了满足中序遍历顺序不变】

在这里插入图片描述
代码如下:

	/**
     * RR情况  进行左旋
     *
     * @param node  出现失衡根节点
     * @return  左旋平衡后的新节点
     */
    private BSTNode<T> rightRightRotation(BSTNode<T> node)
    {
        BSTNode<T> curRoot = node.rightChild;
        node.rightChild=curRoot.leftChild;
        curRoot.leftChild=node;
        return curRoot;
    }
  • RL

A节点由于新插入节点newNode,从而变得失衡。
此时 B->H=max(1+h+1 , h+1)+1=h+3 , A->BF=abs( (h+1)-(h+3) )=2 【其中,h可以为0,也可以为任意数,代表了隐藏子树的高度在这里插入图片描述
由于新插入的节点newNode,是插入在了失衡的节点A的右儿子的左子树中。
所以这种情况,叫做RL

调整依据:根据最小的子问题来理解,假设(h+1)=0

其中由于C的插入,A失衡。

中序遍历顺序为 A-C-B ,所以将C作为根节点,既保持了遍历顺序,又解决了失衡问题。
此时 对失衡节点的右儿子右旋 在对失衡节点左旋

即使失衡节点的右儿子的左儿子为根
在这里插入图片描述
调整方案:

  1. 将C节点作为新的根节点
  2. C的左儿子变为A节点
  3. C的右儿子变为B节点
  4. C原来的左子树β成为A节点的右子树
  5. C原来的右子树γ成为B节点的左子树【为了满足中序遍历顺序不变】

在这里插入图片描述
代码如下:

/**
     * RL情况  先对根节点的右儿子右旋  再对根节点左旋
     * @param node 出现失衡的根节点
     * @return 右旋+左旋后的新的根节点
     */
    private BSTNode<T> rightLeftRotation(BSTNode<T> node)
    {
        node.rightChild=leftLeftRotation(node.rightChild);
        node=rightRightRotation(node);

        return node;
    }

以上告一段落

如何判断失衡和失衡情况

我们讲了如何在不同失衡情况下进行调整,那么随之而来的几个问题

  • 如何判断当前节点是否失衡??
  • 倘若该节点已经失衡,如何判断是四种情况中的哪一种情况?

我们逐一进行分析

问题1 我们的思路是,在节点数据结构中,添加一个 height 类型的成员,用来记录以当前节点为根,树的高度是多少?

判断该根节点是否失衡,即判断该根节点的 getHeight(左儿子) - getHeight(右儿子) >=2? 即可。

/**
 * 搜索二叉树节点类型
 * @param <T>
 */
public class BSTNode<T extends Comparable>{
    public T elem;    //节点元素
    public BSTNode leftChild;  //左儿子
    public BSTNode rightChild; //右儿子
    public int height;  //以该节点为根在树中的深度

    public BSTNode(){}
    public BSTNode(T elem, BSTNode leftChild, BSTNode rightChild) {
        this.elem = elem;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
        this.height=0;
    }
}
	/**
     * 获得以当前节点为根 距离叶子节点的深度
     * @param node
     */
    private int getHeight(BSTNode<T> node)
    {
        if(node==null) return 0;
        return node.height;
    }

除此之外,还要考虑下更新 height 的问题。

思考下,什么时候,节点 height 可能会变化 ?

三种情况

  1. INSERT节点时,newNode的祖先节点可能会变化

在这里插入图片描述

  1. DELETE节点时,被删除节点的祖先节点可能会变化

在这里插入图片描述

  1. 平衡调整时,结构变动的点,可能会出现变化

由于新增加的节点,会影响祖先节点,采取递归进行INSERT操作,同时在每次递归末尾,将当前的节点的树高度进行更新操作。这样,从底向上进行更新,保证了每个节点高度都是基于正确的结果更新。

root.height=Math.max(getHeight(root.leftChild),getHeight(root.rightChild))+1;

对于问题2,递归插入节点,当前状态是由前一次递归状态返回得到的 ,如果当前节点失衡,只需判断前一次的状态是左儿子还是右儿子即可。就可以知道,失衡到底是因为在左儿子插入节点还是在右儿子插入节点。

例如:当前节点A由于插入节点后失衡,A的状态是由 B 递归后返回回来的。则现在确定是L,然后判断下 插入元素G 与 B 的大小关系,G>B 为LR ,G<B为LL。
在这里插入图片描述

INSERT代码如下:

/**
     * 插入元素
     *
     * 根据当前节点 左右子树的深度之差来判断 是否失衡
     *
     * @param root 树的根节点
     * @param elem 插入的元素
     * @return 根节点
     */
    private BSTNode<T> insert(BSTNode<T> root ,T elem)
    {
        if(root==null)
        {
            //进行初始化
            root=new BSTNode<>(elem,null,null);
        }else
        {
            //与根节点进行值得比较
            int cmpNum=elem.compareTo(root.elem);
            if(cmpNum==0) //树中存在相同键的元素 不能插入
            {
                System.out.println("插入失败,表中存在key相同的元素");
            }else if(cmpNum<0) //向左子树中进行插入
            {
                root.leftChild=insert(root.leftChild,elem);
                //前一次递归结束前 已经将左儿子的深度更新
                //现在对当前根节点进行平衡判断
                int balanceFactor=getHeight(root.leftChild)-getHeight(root.rightChild);
                if(Math.abs(balanceFactor)>=2)
                {
                    /**
                     * 进行平衡操作
                     * 【此时需要判断下 要插入的值 与 当前root的左儿子值的大小关系】
                     *
                     * 如果 elem>left  LR情况
                     * 否则是  LL
                     */
                    if(elem.compareTo((T) root.leftChild.elem)<0)
                    {
                        //LL
                        root=leftLeftRotation(root);
                    }else
                    {
                        root=leftRightRotation(root);
                    }
                }
            }else //向右子树进行插入
            {
                root.rightChild=insert(root.rightChild,elem);
                //前一次递归结束前 已经将右儿子的深度更新
                //现在对当前根节点进行平衡判断
                int balanceFactor=getHeight(root.leftChild)-getHeight(root.rightChild);
                if(Math.abs(balanceFactor)>=2)
                {
                    /**
                     * 进行平衡操作
                     * 【此时需要判断下 要插入的值 与 当前root的右儿子值的大小关系】
                     *
                     * 如果 elem>right  RR情况
                     *
                     * 否则是  RL
                     */
                    if(elem.compareTo((T) root.rightChild.elem)<0)
                    {
                        //RL
                        root=rightLeftRotation(root);
                    }else
                    {
                        root=rightRightRotation(root);
                    }
                }
            }
        }
        //对当前根节点深度进行更新
        root.height=Math.max(getHeight(root.leftChild),getHeight(root.rightChild))+1;
        return root;
    }

DELETE

删除节点的话,也是采取递归进行删除的,并且每层更新当前节点高度。只要高度发生变化,可能就会出现节点失衡。

下面介绍下删除节点的三种情况

  • 该节点是叶子节点
    在这里插入图片描述

    该节点无牵无挂,直接删除该节点,同时让其父节点对该节点的指向为NULL。

  • 该节点只有一个儿子

在这里插入图片描述

删除该节点,将该节点的儿子节点 托付 父节点。

代码如下:情况1、2都可以实现

			if(root.rightChild==null)
            {
                root = root.leftChild;
            }else if(root.leftChild==null)
            {
                root = root.rightChild;
            }
  • 该节点有两个儿子

在这里插入图片描述
除了要删除节点,还要保持树的原有遍历顺序不变。

因此删除A节点后,A节点的位置肯定需要一个节点补上去,那么究竟如何补才能满足新的节点 大于 左子树所有点 ,并且 小于 右子树所有点。这是问题的关键所在。

两种选择:

  1. 选择A节点左子树的最大节点 X
    其遍历顺序满足 A的左子树【除了X】——最大节点X——A——A的右子树
  2. 选择A节点右子树的最小节点 Y
    其遍历顺序满足 A的左子树——A——最小节点Y——A的右子树【除了Y】

所以这两个节点,都可以进行选择。

选择后,将删除节点的值换成这个可行节点的值,将可行节点进行删除。由于可行节点是叶子节点,所以删除起来更加方便。

但是问题来了,我们这个树并不是普通的二叉树,而是一个具有节点平衡性质的二叉树。因此,选择可行节点时,尽量不能破坏节点A的平衡。

怎么办呢?一个好的办法就是,比较下节点A的左子树和右子树的高度,从高度高的一棵树上选取节点。这样可以尽最大努力保持平衡,如果这样的选取策略都会失衡,那么我们只能在选取节点后,使用平衡旋转策略了调整了。

代码如下:

				//被删除节点左右子树 均不为空
                if(getHeight(root.leftChild)>=getHeight(root.rightChild))
                {
                	//获得左子树最大节点
                    BSTNode<T> maxNode=getMax(root.leftChild);
                    root.elem=maxNode.elem;
                    //删除掉左子树的这个最大节点【该节点也是叶子节点】
                    root.leftChild=delete(root.leftChild,maxNode.elem);
                }else
                {
                	//获得右子树最小节点
                    BSTNode<T> minNode=getMin(root.rightChild);
                    root.elem=minNode.elem;
                    //删除掉右子树的这个最小节点【该节点也是叶子节点】
                    root.rightChild=delete(root.rightChild,minNode.elem);
                }

现在开始讲讲删除节点后的调整
仔细想想,如果在当前根节点Root的左子树成功删除了某个节点后,Root根节点变得失衡了。什么原因?左子树的高度必然是减小了,否则如果不变的话,就不会出现失衡

换种说法就是,在左子树删除一个节点【树高度减小】 ,那么我们得把高度过高的右子树进行旋转平衡操作,使其高度也变小,才会重新平衡。所以此时,我们只需判断下,Root的右儿子的左子树和右子树的高度谁更高,不是就知道问题出在哪里了么!!! 也就知道了到底是RL 还是 RR情况。

代码如下:

if(Math.abs(getHeight(root.rightChild)-getHeight(root.leftChild))>=2)
            {
                /**
                 * 若该节点失衡
                 * 又因为是在该节点的左子树上删除了节点
                 * 所以 右子树必然深度过大
                 *
                 * 做法:比较下当前根节点的右儿子的左子树和右子树深度关系
                 *
                 * 两种情况:
                 * 1、左子树H > 右子树H 则情况为 RL
                 * 2、左子树H =< 右子树H 则情况为 RR
                 */
                if(getHeight(root.rightChild.leftChild)>getHeight(root.rightChild.rightChild))
                {
                    root=rightLeftRotation(root);
                }else
                {
                    root=rightRightRotation(root);
                }
            }

DELETE完整代码如下:

/**
     * 删除某个元素
     * 删除某个节点以后 可能会造成祖先的某个节点失衡
     *
     * @param root
     * @param elem
     * @return
     */
    private BSTNode<T> delete(BSTNode<T> root,T elem)
    {
        if(root==null)
        {
            System.out.println("节点不存在,删除失败");
            return null;
        }
        int cmp=elem.compareTo(root.elem);
        if(cmp==0) //找到节点进行删除
        {
            if(root.rightChild==null)
            {
                root = root.leftChild;
            }else if(root.leftChild==null)
            {
                root = root.rightChild;
            }else
            {
                /**
                 * 说明被删除节点 左右子树均不为空
                 *
                 * 删除的思路:左子树更深,则使用左子树的最大节点。或者 右子树更深,则使用右子树的最小节点 与被删除节点进行替换 ,
                 *              因为要尽可能缩小两边树的深度差,然后删除这个最大或者最小的叶子节点即可
                 */
                if(getHeight(root.leftChild)>=getHeight(root.rightChild))
                {
                    BSTNode<T> maxNode=getMax(root.leftChild);
                    root.elem=maxNode.elem;
                    //删除掉左子树的这个最大节点【该节点也是叶子节点】
                    root.leftChild=delete(root.leftChild,maxNode.elem);
                }else
                {
                    BSTNode<T> minNode=getMin(root.rightChild);
                    root.elem=minNode.elem;
                    //删除掉右子树的这个最小节点【该节点也是叶子节点】
                    root.rightChild=delete(root.rightChild,minNode.elem);
                }

            }
        }else if(cmp<0)
        {
            root.leftChild=delete(root.leftChild,elem);
            /**
             * 进行平衡判断
             * 判断该节点左子树删除节点后是否平衡
             */
            if(Math.abs(getHeight(root.rightChild)-getHeight(root.leftChild))>=2)
            {
                /**
                 * 若该节点失衡
                 * 又因为是在该节点的左子树上删除了节点
                 * 所以 右子树必然深度过大
                 *
                 * 做法:比较下当前根节点的右儿子的左子树和右子树深度关系
                 *
                 * 两种情况:
                 * 1、左子树H > 右子树H 则情况为 RL
                 * 2、左子树H =< 右子树H 则情况为 RR
                 */
                if(getHeight(root.rightChild.leftChild)>getHeight(root.rightChild.rightChild))
                {
                    root=rightLeftRotation(root);
                }else
                {
                    root=rightRightRotation(root);
                }
            }

        }else
        {
            root.rightChild=delete(root.rightChild,elem);
            /**
             * 进行平衡判断
             * 判断该节点右子树删除节点后是否平衡
             */
            if(Math.abs(getHeight(root.leftChild)-getHeight(root.rightChild))>=2)
            {
                /**
                 * 右子树删除节点后 当前确实出现了失衡
                 *
                 * 所以是左子树的问题
                 *
                 * 做法:比较下当前根节点的左儿子的左子树和右子树深度关系
                 *
                 * 两种情况:
                 *1、左子树H >= 右子树H 则情况为 LL
                 *2、左子树H < 右子树H 则情况为 LR
                 */
                if(getHeight(root.leftChild.leftChild)>=getHeight(root.leftChild.rightChild))
                {
                    root=leftLeftRotation(root);
                }else
                {
                    root=leftRightRotation(root);
                }
            }

        }
        if(root!=null)
        root.height=Math.max(getHeight(root.rightChild),getHeight(root.leftChild))+1;
        return root;
    }

整个完整版代码如下:

package Data_Structure.find;

import javafx.util.Pair;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 平衡二叉搜索树
 */
public class BBSTree<T extends Comparable<T>> {
    private BSTNode<T> mRoot;

    public BBSTree()
    {
        mRoot=null;
    }

    /**
     * 插入元素
     *
     * 根据当前节点 左右子树的深度之差来判断 是否失衡
     *
     * @param root 树的根节点
     * @param elem 插入的元素
     * @return 根节点
     */
    private BSTNode<T> insert(BSTNode<T> root ,T elem)
    {
        if(root==null)
        {
            //进行初始化
            root=new BSTNode<>(elem,null,null);
        }else
        {
            //与根节点进行值得比较
            int cmpNum=elem.compareTo(root.elem);
            if(cmpNum==0) //树中存在相同键的元素 不能插入
            {
                System.out.println("插入失败,表中存在key相同的元素");
            }else if(cmpNum<0) //向左子树中进行插入
            {
                root.leftChild=insert(root.leftChild,elem);
                //前一次递归结束前 已经将左儿子的深度更新
                //现在对当前根节点进行平衡判断
                int balanceFactor=getHeight(root.leftChild)-getHeight(root.rightChild);
                if(Math.abs(balanceFactor)>=2)
                {
                    /**
                     * 进行平衡操作
                     * 【此时需要判断下 要插入的值 与 当前root的左儿子值的大小关系】
                     *
                     * 如果 elem>left  LR情况
                     * 否则是  LL
                     */
                    if(elem.compareTo((T) root.leftChild.elem)<0)
                    {
                        //LL
                        root=leftLeftRotation(root);
                    }else
                    {
                        root=leftRightRotation(root);
                    }
                }
            }else //向右子树进行插入
            {
                root.rightChild=insert(root.rightChild,elem);
                //前一次递归结束前 已经将右儿子的深度更新
                //现在对当前根节点进行平衡判断
                int balanceFactor=getHeight(root.leftChild)-getHeight(root.rightChild);
                if(Math.abs(balanceFactor)>=2)
                {
                    /**
                     * 进行平衡操作
                     * 【此时需要判断下 要插入的值 与 当前root的右儿子值的大小关系】
                     *
                     * 如果 elem>right  RR情况
                     *
                     * 否则是  RL
                     */
                    if(elem.compareTo((T) root.rightChild.elem)<0)
                    {
                        //RL
                        root=rightLeftRotation(root);
                    }else
                    {
                        root=rightRightRotation(root);
                    }
                }
            }
        }
        //对当前根节点深度进行更新
        root.height=Math.max(getHeight(root.leftChild),getHeight(root.rightChild))+1;
        return root;
    }
    public void insert(T elem)
    {
        mRoot=insert(mRoot,elem);
    }


    /**
     * 删除某个元素
     * 删除某个节点以后 可能会造成祖先的某个节点失衡
     *
     * @param root
     * @param elem
     * @return
     */
    private BSTNode<T> delete(BSTNode<T> root,T elem)
    {
        if(root==null)
        {
            System.out.println("节点不存在,删除失败");
            return null;
        }
        int cmp=elem.compareTo(root.elem);
        if(cmp==0) //找到节点进行删除
        {
            if(root.rightChild==null)
            {
                root = root.leftChild;
            }else if(root.leftChild==null)
            {
                root = root.rightChild;
            }else
            {
                /**
                 * 说明被删除节点 左右子树均不为空
                 *
                 * 删除的思路:左子树更深,则使用左子树的最大节点。或者 右子树更深,则使用右子树的最小节点 与被删除节点进行替换 ,
                 *              因为要尽可能缩小两边树的深度差,然后删除这个最大或者最小的叶子节点即可
                 */
                if(getHeight(root.leftChild)>=getHeight(root.rightChild))
                {
                    BSTNode<T> maxNode=getMax(root.leftChild);
                    root.elem=maxNode.elem;
                    //删除掉左子树的这个最大节点【该节点也是叶子节点】
                    root.leftChild=delete(root.leftChild,maxNode.elem);
                }else
                {
                    BSTNode<T> minNode=getMin(root.rightChild);
                    root.elem=minNode.elem;
                    //删除掉右子树的这个最小节点【该节点也是叶子节点】
                    root.rightChild=delete(root.rightChild,minNode.elem);
                }

            }
        }else if(cmp<0)
        {
            root.leftChild=delete(root.leftChild,elem);
            /**
             * 进行平衡判断
             * 判断该节点左子树删除节点后是否平衡
             */
            if(Math.abs(getHeight(root.rightChild)-getHeight(root.leftChild))>=2)
            {
                /**
                 * 若该节点失衡
                 * 又因为是在该节点的左子树上删除了节点
                 * 所以 右子树必然深度过大
                 *
                 * 做法:比较下当前根节点的右儿子的左子树和右子树深度关系
                 *
                 * 两种情况:
                 * 1、左子树H > 右子树H 则情况为 RL
                 * 2、左子树H =< 右子树H 则情况为 RR
                 */
                if(getHeight(root.rightChild.leftChild)>getHeight(root.rightChild.rightChild))
                {
                    root=rightLeftRotation(root);
                }else
                {
                    root=rightRightRotation(root);
                }
            }

        }else
        {
            root.rightChild=delete(root.rightChild,elem);
            /**
             * 进行平衡判断
             * 判断该节点右子树删除节点后是否平衡
             */
            if(Math.abs(getHeight(root.leftChild)-getHeight(root.rightChild))>=2)
            {
                /**
                 * 右子树删除节点后 当前确实出现了失衡
                 *
                 * 所以是左子树的问题
                 *
                 * 做法:比较下当前根节点的左儿子的左子树和右子树深度关系
                 *
                 * 两种情况:
                 *1、左子树H >= 右子树H 则情况为 LL
                 *2、左子树H < 右子树H 则情况为 LR
                 */
                if(getHeight(root.leftChild.leftChild)>=getHeight(root.leftChild.rightChild))
                {
                    root=leftLeftRotation(root);
                }else
                {
                    root=leftRightRotation(root);
                }
            }

        }
        if(root!=null)
        root.height=Math.max(getHeight(root.rightChild),getHeight(root.leftChild))+1;
        return root;
    }
    public void delete(T elem)
    {
        mRoot=delete(mRoot,elem);
    }


    /**
     * 获得以当前节点为根 距离叶子节点的深度
     * @param node
     */
    private int getHeight(BSTNode<T> node)
    {
        if(node==null) return 0;
        return node.height;
    }

    public void InOrderFind()
    {
        InOrderFind(mRoot);
    }
    /**
     * 中序遍历
     * @param root
     */
    private void InOrderFind(BSTNode<T> root)
    {
        if(root==null) return ;
        InOrderFind(root.leftChild);
        System.out.print(root.elem+" ");
        InOrderFind(root.rightChild);
    }


    /**
     * RR情况  进行左旋
     *
     * 并且对 变化节点【原根节点 AND 新根节点】的深度 进行更新
     *
     * @param node  出现失衡根节点
     * @return  左旋平衡后的新节点
     */
    private BSTNode<T> rightRightRotation(BSTNode<T> node)
    {
        BSTNode<T> curRoot = node.rightChild;
        node.rightChild=curRoot.leftChild;
        curRoot.leftChild=node;

        node.height=Math.max(getHeight(node.leftChild),getHeight(node.rightChild))+1;
        curRoot.height=Math.max(getHeight(curRoot.rightChild),getHeight(curRoot.leftChild))+1;

        return curRoot;
    }

    /**
     * LL情况  进行右旋
     *
     * 并且对 变化节点【原根节点 AND 新根节点】的深度 进行更新
     *
     * @param node 出现失衡的根节点
     * @return  右旋后的新节点
     */
    private BSTNode<T> leftLeftRotation(BSTNode<T> node)
    {
        BSTNode<T> curRoot = node.leftChild;
        node.leftChild=curRoot.rightChild;
        curRoot.rightChild=node;

        node.height=Math.max(getHeight(node.leftChild),getHeight(node.rightChild))+1;
        curRoot.height=Math.max(getHeight(curRoot.rightChild),getHeight(curRoot.leftChild))+1;

        return curRoot;
    }

    /**
     * LR情况  先对根节点的左儿子左旋  再对根节点右旋
     * @param node 出现失衡的根节点
     * @return  左旋+右旋后的新的根节点
     */
    private BSTNode<T> leftRightRotation(BSTNode<T> node)
    {
        node.leftChild=rightRightRotation(node.leftChild);
        node=leftLeftRotation(node);
        return node;
    }

    /**
     * RL情况  先对根节点的右儿子右旋  再对根节点左旋
     * @param node 出现失衡的根节点
     * @return 右旋+左旋后的新的根节点
     */
    private BSTNode<T> rightLeftRotation(BSTNode<T> node)
    {
        node.rightChild=leftLeftRotation(node.rightChild);
        node=rightRightRotation(node);

        return node;
    }

    /**
     * 打印二叉树
     * @param root
     */
    private void print(BSTNode<T> root,String str)
    {

        Queue<Pair<BSTNode<T>,String>> queue=new LinkedList<Pair<BSTNode<T>,String>>();

        queue.add(new Pair<BSTNode<T>,String>(root,str));
        while(!queue.isEmpty())
        {
            int len=queue.size();
            for(int i=0;i<len;i++)
            {
                Pair<BSTNode<T>,String> p=queue.poll();
                System.out.print(p.getKey().elem+"-"+p.getValue()+"-"+p.getKey().height+"  ");
                if(p.getKey().leftChild!=null)
                {
                    queue.add(new Pair<BSTNode<T>,String>(p.getKey().leftChild,p.getValue()+"L"));
                }
                if(p.getKey().rightChild!=null)
                {
                    queue.add(new Pair<BSTNode<T>,String>(p.getKey().rightChild,p.getValue()+"R"));
                }
            }
            System.out.println();
        }

    }
    public void print()
    {
        print(mRoot,"root");
    }

    /**
     * 获取以当前节点为根的子树的最小值
     * @param root 根节点
     * @return 最小值
     */
    private BSTNode<T> getMin(BSTNode<T> root)
    {
        if(root.leftChild==null)
        {
            return root;
        }
        return getMin(root.leftChild);
    }
    public BSTNode<T> getMin()
    {
        return getMin(mRoot);
    }

    /**
     * 获取以当前节点为根的子树的最大值
     * @param root 根节点
     * @return 最大值
     */
    private BSTNode<T> getMax(BSTNode<T> root)
    {
        if(root.rightChild==null)
        {
            return root;
        }
        return getMax(root.rightChild);
    }
    public BSTNode<T> getMax()
    {
        return getMax(mRoot);
    }

}

Test

public class Test {
    public static void main(String[] args) {
        int []arr={3,5,1,22,18,35,19,15};
        BBSTree<Integer> bbsTree=new BBSTree<>();
        for(int i : arr)
        {
            bbsTree.insert(i);
        }

        System.out.println("中序遍历结果为:");
        bbsTree.InOrderFind();
        System.out.println();
        System.out.println("整个二叉搜索树的结构为:");
        bbsTree.print();
        System.out.println();


        System.out.println("删除元素5");
        bbsTree.delete(5);
        System.out.println("中序遍历结果为:");
        bbsTree.InOrderFind();

        System.out.println();
        System.out.println("整个二叉搜索树的结构为:");
        bbsTree.print();
    }
}


结果如下:
中序遍历结果为:
1 3 5 15 18 19 22 35 
整个二叉搜索树的结构为:
18-root-4  
3-rootL-3  22-rootR-2  
1-rootLL-1  5-rootLR-2  19-rootRL-1  35-rootRR-1  
15-rootLRR-1  

删除元素5
中序遍历结果为:
1 3 15 18 19 22 35 
整个二叉搜索树的结构为:
18-root-3  
3-rootL-2  22-rootR-2  
1-rootLL-1  15-rootLR-1  19-rootRL-1  35-rootRR-1  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值