平衡二叉树:
在二叉排序树的创建中,若创建序列不恰当,可能会导致二叉排序树高度过大,甚至变成单链表
如序列1 2 3 4 5 6,显然每次插入新节点,都在右子树上插入,形成一个单链表
此时对该树的查询效率降低,没有发挥出二叉排序树的优势,为了解决这一问题,引入平衡二叉树
平衡二叉树满足以下两个条件
(1)是二叉排序树
(2)左右子树均为平衡二叉树,且高度差不大于1
空树也为平衡二叉树,且一般将某个节点的左右子树高度差称为该节点的平衡因子,记作BF
实现思路:
对于二叉树的失衡情况,可以简化为以下四种
LL型:
RR型:
LR型:
RL型:
这四种情况均需利用到二叉树的左旋和右旋进行处理,下面先介绍左旋和右旋
左旋:
如图可以看出,左旋是将整棵树向左边(逆时针)进行旋转,其中,根节点右子树的左子树,在旋转后变成根节点左子树的右子树,具体算法如下:
(1)以当前节点的值创建新节点
(2)把新的节点的左子树设置为当前节点的左子树
(3)把新的节点的右子树设置为当前节点右子树的左子树
(4)把当前节点的值修改为右孩子的值
(5)把当前节点的右子树设置为右孩子的右子树
(6)把当前节点的左孩子设置为新节点
经过这样的操作后,原先二叉树中会有一个节点(在本例中为3)会由于没有任何指针指向它,被Java的GC自动回收
注意的是,左旋和右旋是针对某个节点进行的,也就是说,算法中的当前节点即为进行左旋和右旋的节点,如本例中当前节点为根节点,此时根节点的平衡因子的绝对值大于1
右旋:
与左旋正好相反,具体算法如下:
(1)以当前节点的值创建新节点
(2)把新的节点的右子树设置为当前节点的右子树
(3)把新的节点的左子树设置为当前节点左子树的右子树
(4)把当前节点的值修改为左孩子的值
(5)把当前节点的左子树设置为左孩子的左子树
(6)把当前节点的右孩子设置为新节点
对于LL型和RR型,只需要从下往上找到第一个不平衡(平衡因子的绝对值大于1)的节点,进行左旋或右旋即可
对于LR型和RL型,需要进行两次旋转,具体如下:
LR型:先对左孩子进行左旋,再对根节点进行右旋
RL型:先对右孩子进行右旋,再对根节点进行左旋
为了判断节点是否平衡,还需设计获取子树高度的方法,按照树高等于左右子树的最大高度 + 1的思想,设计递归方法进行计算即可,较为简单
对于四种类型的判断,首先比较左右子树高度的关系,确定左旋还是右旋,然后进一步判断其左孩子(右孩子)的左右子树高度的关系,来确定是否需要两次旋转,四种情况如下:
LL型:左子树与右子树高度之差大于1,且左子树的左子树高度不小于左子树的右子树高度
LR型:左子树与右子树高度之差大于1,且左子树的左子树高度小于左子树的右子树高度
RR型:右子树与左子树高度之差大于1,且右子树的右子树高度不小于右子树的左子树高度
RL型:右子树与左子树高度之差大于1,且右子树的右子树高度小于右子树的左子树高度
在添加节点和删除节点中,都通过递归,实现了从下往上的平衡处理
代码实现:
节点类AVLTreeNode:
public class AVLTreeNode {
public int data;
public AVLTreeNode left;
public AVLTreeNode right;
public AVLTreeNode(int data) {
this.data = data;
}
//返回左子树高度
public int getLeftHeight() {
if (left == null) {
return 0;
}
return left.getHeight();
}
//返回右子树高度
public int getRightHeight() {
if (right == null) {
return 0;
}
return right.getHeight();
}
//返回当前节点为根节点的树的高度
public int getHeight() {
return Math.max(left == null ? 0 : left.getHeight(), right == null ? 0 : right.getHeight()) + 1;
}
//左旋转
public void leftRotate() {
AVLTreeNode newNode = new AVLTreeNode(data);
newNode.left = left;
newNode.right = right.left;
data = right.data;
right = right.right;
left = newNode;
}
//右旋转
public void rightRotate() {
AVLTreeNode newNode = new AVLTreeNode(data);
newNode.right = right;
newNode.left = left.right;
data = left.data;
left = left.left;
right = newNode;
}
//添加节点
public void add(AVLTreeNode node) {
if (node == null) {
return;
}
if (node.data < this.data) {
if (this.left == null) {
this.left = node;
} else {
this.left.add(node);
}
} else {
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
if (getRightHeight() - getLeftHeight() > 1) {
if (right.getLeftHeight() > right.getRightHeight()) {
right.rightRotate();
}
leftRotate();
} else if (getLeftHeight() - getRightHeight() > 1) {
if (left.getRightHeight() > left.getLeftHeight()) {
left.leftRotate();
}
rightRotate();
}
}
//查找待删除的节点
public AVLTreeNode search(int data) {
if (data == this.data) {
return this;
} else if (data < this.data) {
if (this.left == null) {
return null;
}
return this.left.search(data);
} else {
if (this.right == null) {
return null;
}
return this.right.search(data);
}
}
//查找待删除节点的父节点
public AVLTreeNode searchParent(int data) {
if (this.left != null && this.left.data == data || this.right != null && this.right.data == data) {
return this;
} else {
if (this.left != null && data < this.data) {
return this.left.searchParent(data);
} else if (this.right != null && data >= this.data) {
return this.right.searchParent(data);
} else {
return null;
}
}
}
//删除节点后平衡二叉树递归方法
public void balanceAfterDelete(AVLTreeNode parentNode) {
if (parentNode.data < this.data) {
this.left.balanceAfterDelete(parentNode);
} else if (parentNode.data > this.data) {
this.right.balanceAfterDelete(parentNode);
}
if (getRightHeight() - getLeftHeight() > 1) {
if (right.getLeftHeight() > right.getRightHeight()) {
right.rightRotate();
}
leftRotate();
} else if (getLeftHeight() - getRightHeight() > 1) {
if (left.getRightHeight() > left.getLeftHeight()) {
left.leftRotate();
}
rightRotate();
}
}
//中序遍历
public void inOrder() {
if (this.left != null) {
this.left.inOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.inOrder();
}
}
@Override
public String toString() {
return "AVLTreeNode{" + "data=" + data + '}';
}
}
平衡二叉树类AVLTree:
public class AVLTree {
public AVLTreeNode root;
//返回高度
public int getHeight() {
return root.getHeight();
}
//添加节点
public void add(AVLTreeNode node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
//查找待删除的节点
public AVLTreeNode search(int data) {
if (root == null) {
return null;
} else {
return root.search(data);
}
}
//查找待删除节点的父节点
public AVLTreeNode searchParent(int data) {
if (root == null) {
return null;
} else {
return root.searchParent(data);
}
}
//查找以node为根节点的二叉排序树的最小节点的值
public int deleteRightTreeMin(AVLTreeNode node) {
AVLTreeNode targetNode = node;
while (targetNode.left != null) {
targetNode = targetNode.left;
}
delete(targetNode.data);
return targetNode.data;
}
//删除节点
public void delete(int data) {
if (root == null) {
return;
} else {
AVLTreeNode targetNode = search(data);
if (targetNode == null) {
return;
}
if (root.left == null && root.right == null) {
root = null;
return;
}
AVLTreeNode parentNode = searchParent(data);
if (targetNode.left == null && targetNode.right == null) {
if (parentNode.left != null && parentNode.left.data == data) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.data == data) {
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {
targetNode.data = deleteRightTreeMin(targetNode.right);
} else {
if (targetNode.left != null) {
if (parentNode != null) {
if (parentNode.left.data == data) {
parentNode.left = targetNode.left;
} else {
parentNode.right = targetNode.left;
}
} else {
root = targetNode.left;
}
} else {
if (parentNode != null) {
if (parentNode.left.data == data) {
parentNode.left = targetNode.right;
} else {
parentNode.right = targetNode.right;
}
} else {
root = targetNode.right;
}
}
}
balanceAfterDelete(parentNode);
}
}
//删除节点后平衡二叉树递归方法
public void balanceAfterDelete(AVLTreeNode parentNode) {
root.balanceAfterDelete(parentNode);
}
//中序遍历
public void inOrder() {
if (this.root != null) {
this.root.inOrder();
} else {
System.out.println("二叉树为空");
}
}
}
|