前言
此博客仅是本人复习时所记录
文章较长,但内容全面。涉及到了平衡二叉搜索树的插入和删除讲解,以及代码实现。
最后附上源码,本人调试无问题,但不能保证完全不出错。
欢迎大家,指出错误。
平衡二叉搜索树
平衡二叉搜索树(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-γ
调整方案:
- 将B节点作为新的根节点
- B的左子树α不变
- B的右儿子变为A节点
- 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-δ
调整方案:
- 将C节点作为新的根节点
- C的左儿子变为B节点
- C的右儿子变为A节点
- C原来的左子树β成为B节点的右子树
- 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作为根节点,既保持了遍历顺序,又解决了失衡问题。
此时 对失衡节点进行左旋(逆时针)
即使失衡节点的右儿子为根
调整方案:
- 将B节点作为新的根节点
- B的右子树β不变
- B的左儿子变为A节点
- 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作为根节点,既保持了遍历顺序,又解决了失衡问题。
此时 对失衡节点的右儿子右旋 在对失衡节点左旋
即使失衡节点的右儿子的左儿子为根
调整方案:
- 将C节点作为新的根节点
- C的左儿子变为A节点
- C的右儿子变为B节点
- C原来的左子树β成为A节点的右子树
- 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 可能会变化 ?
三种情况
- INSERT节点时,newNode的祖先节点可能会变化
- DELETE节点时,被删除节点的祖先节点可能会变化
- 平衡调整时,结构变动的点,可能会出现变化
由于新增加的节点,会影响祖先节点,采取递归进行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节点的位置肯定需要一个节点补上去,那么究竟如何补才能满足新的节点 大于 左子树所有点 ,并且 小于 右子树所有点。这是问题的关键所在。
两种选择:
- 选择A节点左子树的最大节点 X
其遍历顺序满足 A的左子树【除了X】——最大节点X——A——A的右子树 - 选择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