以下代码和实现思路来源于B站视频“恋上数据结构与算法”,地址: https://www.bilibili.com/video/BV13v41167ix?
前期准备
BinaryTree的Node节点类中添加两个方法,用于判断当前节点为其父节点的左孩子节点或右孩子节点
//判断某个节点为父节点的左子节点
public boolean isLeftChild() {
return parent!=null&&parent.left==this;
}
public boolean isRightChild() {
return parent!=null&&parent.right==this;
}
AVLTree继承自二叉搜索树,二叉搜索树原来的add方法中对添加的元素按照一定逻辑添加,但并没有对元素失去平衡进行判断和处理。所以在BinarySearchTree中添加一个AddAter方法,用于对节点添加后进行失衡判断和失衡处理。
//交给AVLTree类去实现的方法,在普通二叉树中没有函数体
protected void AfterAdd(Node<E> node) {
}
public void add(E element) {
elementNotNullCheck(element);//先检查元素是否为空
//当root节点为空时
if(root==null) {
root=CreateNode(element,null);//此处不加<>会出现警告
size++;
AfterAdd(root);//调用函数对添加后的节点进行失衡处理
return;
}
int cmp = 0;//定义为0避免警告
Node<E> node=root;
Node<E> parent = null;//初始为空避免警告
//找到父节点
while(node!=null) {
parent=node;//保存父节点
cmp=compare(element, node.element);
if(cmp>0) {//如果待插入元素较大
node=node.right;
}else if(cmp<0) {//如果待插入元素较小
node=node.left;
}else {//如果相等,覆盖
node.element=element;
return;
}
}
//将当前元素插入到父节点的指定位置
Node<E> newNode= CreateNode(element, parent);
if(cmp>0) {
parent.right= newNode;
}
if(cmp<0) {
parent.left= newNode;
}
size++;
AfterAdd(newNode);//调用函数对添加后的节点进行失衡处理
}
在AVLTree中重新定义一个AVLNode类,它添加一个height属性
private static class AVLNode<E> extends Node<E>{
//height默认值为1,叶子节点的高度为1
int height=1;
public AVLNode(E element, Node<E> parent) {
super(element, parent);
}
}
失衡判断与处理
分析可知,在对AfterAdd方法的函数体进行编写时需要有更新节点高度的方法,和求平衡因子的方法。两个方法都写在AVLNode中,便于方法height属性
public void UpdateHeight(Node<E> node){
int leftHeight=left==null?0:((AVLNode<E>)left).height;
int rightHeight=height==null?((AVLNode<E>)right).height;
return 1+Math.max(leftHeight,rightHeight);
}
public int BalanceFactor(Node<E> node){
int leftHeight=left==null?0:((AVLNode<E>)left).height;
int rightHeight=height==null?((AVLNode<E>)right).height;
return leftHeight-rightHeight;
}
在此基础上,可以很容易地在AVLTree类中写出判断节点是否平衡的方法和更新节点高度的方法
private void updateHeight(Node<E> node){
((AVLNode<E>)node).UpdateHeight;
}
private boolean isBalance(Node<E> node){
return Maht.abs(BalanceFanctor(node))<=1;
}
于是在AddAfter方法中就可以写出框架
protected void AfterAdd(Node<E> node) {
while((node=node.parent)!=null) {
if(isBalanced(node)) {//如果该节点是平衡的
updateHeight(node);//更新高度
}else {
//恢复平衡
reBalance(node);
break;
}
}
}
恢复平衡
现在的主要工作就是编写恢复平衡方法,在恢复平衡方法中,需要判断节点失衡类型。在判断失衡类型的时候,需要知道是左子节点还是右子节点导致的失衡,分析可知,如果节点失衡,那么一定是由其孩子节点中较高的子树导致的,所以可以在AVLNode中编写一个求较高孩子节点的方法:
public Node<E> tallerChild(){
int leftHeight=left==null?0:((AVLNode<E>)left).height;
int rightHeight=right==null?0:((AVLNode<E>)right).height;
if(leftHeight>rightHeight) return left;
if(leftHeight<rightHeight) return right;
//当左右子树高度相等时,按如下策略返回
return isLeftChild()?left:right;
}
如果定义好左旋方法(rotateLeft)和右旋方法(rotateRight)后,恢复平衡的代码框架如下:
private void reBalance(Node<E> grand){
Node<E> parent=((AVLNode<E>)grand).tallerChild();
Node<E> node=((AVLNode<E>)parent).tallerChild();
if(parent.isLeftChild()){
if(node.isLeftChild()){//LL
rotateRight(grand);
}else{//LR
rotateLeft(parent);
rotateRight(grand);
}
}else{
if(node.isLeftChild()){//RL
rotateRight(parent);
rotateLeft(grand);
}else{//RR
rotateLeft(grand);
}
}
}
左旋和右旋方法需要改变很多节点,同时还需要更新height;
private void rotateLeft(Node<E> grand) {
Node<E> parent=grand.right;
Node<E> child=parent.left;
grand.right=child;
parent.left=grand;
parent.parent=grand.parent;
if(grand.isLeftChild()){
grand.parent.left=parent;
}else if(grand.isRightChild()){
grand.parent.right=parent;
}else{
root=parent;
}
if(child!=null){
child.parent=grand;
}
grand.parent=parent;
updateHeight(grand);
updateHeight(parent);
}
```cpp
private void rotateRight(Node<E> grand) {
Node<E> parent=grand.left;
Node<E> child=parent.right;
grand.left=child;
parent.right=grand;
parent.parent=grand.parent;
if(grand.isLeftChild()){
grand.parent.left=parent;
}else if(grand.isRightChild()){
grand.parent.right=parent;
}else{
root=parent;
}
if(child!=null){
child.parent=grand;
}
grand.parent=parent;
updateHeight(grand);
updateHeight(parent);
}
对比分析可知,rotateRight和rotate有很多共同代码,于是提取出共同部分,最后为:
private void rotateRight(Node<E> grand) {
Node<E> parent =grand.left;
Node<E> child = parent.right;
grand.left=child;
parent.right=grand;
AfterRotate(grand, parent, child);
}
private void rotateLeft(Node<E> grand) {
Node<E> parent =grand.right;
Node<E> child = parent.left;
grand.right=child;
parent.left=grand;
AfterRotate(grand, parent, child);
}
//将rotateLeft和rotateRight中的公共部分提取出来放到AfterRotate函数中
private void AfterRotate(Node<E> grand,Node<E> parent,Node<E> child) {
//让parent成为子树的根节点
parent.parent=grand.parent;
if(grand.isLeftChild()) {
grand.parent.left=parent;
}else if(grand.isRightChild()) {
grand.parent.right=parent;
}else {//grand是root节点
root=parent;
}
//更新child的parent
if(child!=null) {
child.parent=grand;
}
//更新grand的parent
grand.parent=parent;
//更新高度
updateHeight(grand);
updateHeight(parent);
}