一、什么是平衡二叉树
平衡二叉树是由前苏联的两位数学家G.M.Adelse-Velskil和E.M.Landis提出,因此一般也称作AVL树,AVL树本质还是一棵二叉查找树,只是在其基础上增加了“平衡”的要求。所谓平衡是指,对AVL树的任意结点来说,其左子树与右子树的高度之差的绝对值不超过1,其中左子树与右子树的高度因子之差称为平衡因子。
二叉查找树,又称二叉排序树,亦称二叉搜索树。是数据结构中的一类。在一般情况下,查询效率比链表结构要高。给定值的比较次数等于给定值节点在二叉排序树中的层数。如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2(n+1),其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。
二、平衡二叉树的原理
1、平衡因子
平衡因子,当前节点的左子树高度减去右子树高度,即为当前节点的平衡因子。如图所示,虽然这不是一棵平衡二叉树,或者说待平衡的二叉查找树。
2、失衡点
当前树中,平衡因子绝对值大于1的节点,即为当前树的失衡点。如图1中,A节点即为失衡点。
平衡因子大于0,说明左子树高度高于右子树;反之,平衡因子小于0,说明左子树高度低于右子树。
3、旋转
上文提到过,平衡二叉树是在二叉查找树基础上增加了“平衡”的要求。如果仅是无序的去向树中填充数据,我们无法预判到哪些数据会填入树中,如图1中,当ABCDE的树生成完成,我们没有办法去预估数据F的填充,我们的平衡树结构很容易遭到破坏,为了修复树的平衡,我们可以通过对树进行简单的修复来让其重新恢复到平衡,由此引出了旋转的概念。
旋转有两种形式,右旋转和左旋转。
提一下根结点的概念,根结点是树的一个组成部分,也叫树根。所有非空的二叉树中,都有且仅有一个根结点简介。它是同一棵树中除本身外所有结点的祖先,没有父结点。
①右旋转
当平衡因子大于1,此时左子树高度高于右子树,如图所示,为了达到平衡,肯定是想办法将左子树的部分数据移至右子树
我们要做的很简单:
1、用失衡点A的左子结点B代替A,即切断A与B的关联关系,并将A的父结点中对于A的指向修改为指向B(图中未画出A的父结点);
2、将左子结点的右子树,即以D为根的树,变为失衡点A的左子树;
3、将修改后的A结点作为原左子结点B的右子树。
经过这三个步骤,树重新平衡,平衡因子绝对值全部小于等于1。同时,可以看到数据的存储结构的改变仿佛是将A结点向右侧进行了转动。
②左旋转
左旋转就是右子树高了,通过与右旋转相反的操作,使右子树数据转移至左子树,就不画图讲解了
1、用失衡点的右子结点代替失衡点;
2、将右子结点的左子树,变为失衡点的右子树;
3、将修改后的失衡点作为原右子结点的左子树。
③旋转的原理
旋转的原理其实很简单,拿上文右旋转的图片来举例
对于第一步,如果A还有父结点,我们可以得到 A父结点数据 > A结点数据 > B结点数据 是成立的,如果A是整棵树的根那更好,A与B之间进行右旋,B的左子树与A的右子树结构未发生改变,不会影响数据排序,唯一需要处理的是B的右子树。不要问A的左子树去哪了。
首先二叉查找树的前提是存储的数据可排序,如图片中就以数字为例,除叶子结点以外,每一个结点左子结点的数据都是小于自身的,右子结点的数据都是大于自身的。
那么可以得到 B结点数据 < B右子树任意数据 < A节点数据。那么D或以D为根的树,作为B的右子树和作为A左子树其实都不会对数据结构的有序性造成任何影响。
4、旋转的使用
现在我们回头看 1、平衡因子 中的图一,当我们尝试用右旋转修复树的平衡,却发现仍然不能让他变成一颗平衡二叉树,这是为什么呢?
原来这是以D为根的树导致失衡,那么不论如何旋转,以D为根的树都会导致失衡,那么我们要做的就是破坏D树的结构,让D树把数据吐出来,好让我们有足够数量的数据进行树的平衡。
这时候我们不妨将B结点进行左旋转,然后再去右旋转A结点。
好在这是有规律可言的:
1、我们将失衡点的平衡因子大于1的,也就是左侧树过高的记为L类型;将失衡点的平衡因子小于-1的,也就是右侧树过高的记为R类型;
2、L类型的话,我们再去判断他的左子树平衡因子,若大于0说明左子树的左子树更高,记为LL类型;若小于0说明左子树的右子树更高,记为LR类型
R类型的话,去判断他的右子树平衡因子,分别记为RL与RR;
3、根据不同情况选择对应的旋转方式
LL:右旋转失衡点
RR:左旋转失衡点
LR:先左旋转失衡点的左子结点,再右旋转失衡点
RL:先右旋转失衡点的右子结点,再左旋转失衡点
5、数据的移除
对于平衡二叉树数据的移除,前提条件当然是这棵树是一颗平衡二叉树,如图
如果我们要移除C点数据,根据二叉查找树的有序性,我们可以得到
1、C的左子树任意数据 < C自身数据 < C的右子树的任意数据
2、C左子树的最大数据为以E为根树的最右叶子结点,图中为H;C右子树的最小数据为以F为根的最左叶子结点,图中为F
那么,我们为了不破坏树的有序结构,要做的也很简单,使用H的值或者F的值来替代C的值,然后移除H或者F,之后再判断是否需要旋转平衡即可。
三、Java代码实现
参考多篇文章资料自己进行的实现整合,存在冗余也请见谅,存在问题感谢指出
import java.io.IOException;
public class AVLNode<T extends Comparable> {
AVLNode<T> parent;
AVLNode<T> leftChild, rightChild;
T data;
int height;
public AVLNode(AVLNode<T> parent, AVLNode<T> leftChild, AVLNode<T> rightChild, T data) {
this.parent = parent;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.data = data;
this.height = 1;
}
public AVLNode(T data) {
this(null, null, null, data);
}
public AVLNode(AVLNode<T> parent, T data) {
this(parent, null, null, data);
}
public AVLNode() {
this(null, null, null, null);
}
/**
* 右旋转
* (1)节点的左孩子代表此节点
* (2)节点的左孩子的右子树变为节点的左子树
* (3)将此节点作为左孩子节点的右子树。
*
* @param node 失衡点
* @return
*/
public AVLNode<T> rightRotation(AVLNode<T> node) {
if (node == null) {
return null;
}
AVLNode<T> leftChild = node.leftChild;
// 节点的左孩子代表此节点
if (node.parent != null) {
if (node.parent.leftChild == node) {
node.parent.leftChild = leftChild;
} else if (node.parent.rightChild == node) {
node.parent.rightChild = leftChild;
}
}
leftChild.parent = node.parent;
// 节点的左孩子的右子树变为节点的左子树
node.leftChild = leftChild.rightChild;
if (leftChild.rightChild != null) {
leftChild.rightChild.parent = node;
}
// 将此节点作为左孩子节点的右子树
leftChild.rightChild = node;
node.parent = leftChild;
// 重新计算节点高度
node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
leftChild.height = Math.max(getHeight(leftChild.leftChild), getHeight(leftChild.rightChild)) + 1;
return leftChild;
}
/**
* 左旋转
* @param node
* @return
*/
public AVLNode<T> leftRotation(AVLNode<T> node) {
if (node == null) {
return null;
}
AVLNode<T> rightChild = node.rightChild;
if (node.parent != null) {
if (node.parent.leftChild == node) {
node.parent.leftChild = rightChild;
} else if (node.parent.rightChild == node) {
node.parent.rightChild = rightChild;
}
}
rightChild.parent = node.parent;
node.rightChild = rightChild.leftChild;
if (rightChild.leftChild != null) {
rightChild.leftChild.parent = node;
}
rightChild.leftChild = node;
node.parent = rightChild;
node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
rightChild.height = Math.max(getHeight(rightChild.leftChild), getHeight(rightChild.rightChild)) + 1;
return rightChild;
}
public AVLNode<T> put(T data) {
return put(null, this, data);
}
/**
* 新增数据存储
* @param parent
* @param node
* @param data
* @return
*/
public AVLNode<T> put(AVLNode<T> parent, AVLNode<T> node, T data) {
if (node == null || node.data == null) {
node = new AVLNode(parent, data);
return node;
}
if (node.data.compareTo(data) > 0) {
node.leftChild = put(node, node.leftChild, data);
} else if (node.data.compareTo(data) < 0) {
node.rightChild = put(node, node.rightChild, data);
}
node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
// 计算平衡因子判断是否需要旋转平衡
int balanceFactor = getBalanceFactor(node);
if (balanceFactor > 1 && getBalanceFactor(node.leftChild) >= 0) {
node = rightRotation(node);
} else if (balanceFactor > 1 && getBalanceFactor(node.leftChild) < 0) {
node.leftChild = leftRotation(node.leftChild);
node = rightRotation(node);
} else if (balanceFactor < -1 && getBalanceFactor(node.rightChild) > 0) {
node.rightChild = rightRotation(node.rightChild);
node = leftRotation(node);
} else if (balanceFactor < -1 && getBalanceFactor(node.rightChild) <= 0) {
node = leftRotation(node);
}
return node;
}
public AVLNode<T> remove(T data) {
return remove(this, data);
}
/**
* 移除指定数据
* @param parent
* @param data
* @return
*/
public AVLNode<T> remove(AVLNode<T> parent, T data) {
AVLNode<T> node = parent.find(data);
if (node == null)
return node;
// 此处存在冗余,应当修改为左子树最大值为空则用右子树最小值替代,进行相关操作
if (node.data.compareTo(data) >= 0) {
AVLNode<T> leftMaxData = findMaxData(node.leftChild);
leftMaxData.parent.data = leftMaxData.data;
if (node.leftChild == leftMaxData) {
leftMaxData.parent.leftChild = leftMaxData.leftChild;
} else {
leftMaxData.parent.rightChild = leftMaxData.leftChild;
}
if (leftMaxData.leftChild != null) {
leftMaxData.leftChild.parent = leftMaxData.parent;
}
} else {
AVLNode<T> rightMaxData = findMinData(node.rightChild);
node.rightChild.data = rightMaxData.data;
if (node.rightChild == rightMaxData) {
rightMaxData.parent.rightChild = rightMaxData.rightChild;
} else {
rightMaxData.parent.leftChild = rightMaxData.rightChild;
}
if (rightMaxData.rightChild != null) {
rightMaxData.rightChild.parent = rightMaxData.parent;
}
}
parent.height = Math.max(getHeight(parent.leftChild), getHeight(parent.rightChild) + 1);
int balanceFactor = getBalanceFactor(parent);
if (balanceFactor > 1 && getBalanceFactor(parent.leftChild) >= 0) {
parent = rightRotation(parent);
} else if (balanceFactor > 1 && getBalanceFactor(parent.leftChild) < 0) {
parent.leftChild = leftRotation(parent.leftChild);
parent = rightRotation(parent);
} else if (balanceFactor < -1 && getBalanceFactor(parent.rightChild) > 0) {
parent.rightChild = rightRotation(parent.rightChild);
parent = leftRotation(parent);
} else if (balanceFactor < -1 && getBalanceFactor(parent.rightChild) <= 0) {
parent = leftRotation(parent);
}
return parent;
}
public AVLNode<T> find(T data) {
if (this.data.compareTo(data) > 0) {
return this.leftChild == null ? null : this.leftChild.find(data);
} else if (this.data.compareTo(data) < 0) {
return this.rightChild == null ? null : this.rightChild.find(data);
} else {
return this;
}
}
private AVLNode<T> findMaxData(AVLNode<T> node) {
if (node.rightChild != null) {
node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild) - 1);
return findMaxData(node.rightChild);
} else {
return node;
}
}
private AVLNode<T> findMinData(AVLNode<T> node) {
if (node.leftChild != null) {
node.height = Math.max(getHeight(node.leftChild) - 1, getHeight(node.rightChild));
return findMaxData(node.leftChild);
} else {
return node;
}
}
public int getHeight(AVLNode<T> node) {
if (null == node) {
return 0;
} else {
return node.height;
}
}
// 返回节点的平衡因子
public int getBalanceFactor(AVLNode<T> node) {
if (node == null)
return 0;
return getHeight(node.leftChild) - getHeight(node.rightChild);
}
public static void main(String[] args) throws IOException {
int[] i = new int[]{50, 25, 75, 12, 37, 45, 44, 46};
AVLNode<Integer> root = new AVLNode<Integer>();
for (int j = 0; j < i.length; j++) {
if (i[j] == 12) {
System.out.println(1);
}
root = root.put(i[j]);
}
AVLNode tmp = root.remove(45);
System.out.println(root.data);
}
}