- 当创建二叉树时添加的首个节点相对小于整个数列时, 会引起整个二叉树的左右节点不平衡, 严重影响查询速度. 此种情况的解决方案就是平衡二叉树
概述
-
平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree), 又被称为 AVL树, 可以保证查询效率较高
-
平衡二叉树的常见实现有红黑树, AVL, 替罪羊树, Treap, 伸展树等
* 特点: 一棵的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是一棵平衡二叉树
-
当左右子树的高度差超过了1时, 需要进行重新调整平衡树
- 左旋转: 当右子树的层数, 超过了左子树1个层以上时进行
- 右旋转: 当左子树的层数, 超过了右子树1个层以上时进行
- 双旋转: 当符合右旋转条件时, 如果它的左子树的右子树高度大于它的左子树高度时
实例代码
public class AVLTreeApp {
public static void main(String[] args) {
int[] arr = {4, 3, 6, 5, 7, 8}; /** 测试数据1(用于左旋转)*/
//int[] arr = { 10, 12, 8, 9, 7, 6 }; /** 测试数据2(用于右旋转)*/
//int[] arr = { 10, 11, 7, 6, 8, 9 }; /** 测试数据3(用于双旋转)*/
AVLTree avlTree = new AVLTree();
/** 循环添加节点*/
for(int i = 0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
System.out.println("中序遍历:");
avlTree.infixOrder();
System.out.println("树的高度 " + avlTree.getRoot().height());
System.out.println("树的左子树高度 " + avlTree.getRoot().leftHeight());
System.out.println("树的右子树高度 " + avlTree.getRoot().rightHeight());
System.out.println("当前的根节点 " + avlTree.getRoot());
}
}
/** 平衡二叉树*/
class AVLTree {
private Node root;
public Node getRoot() {
return root;
}
/** 添加节点*/
public void add(Node node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
/** 中序遍历*/
public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("二叉排序树为空,不能遍历");
}
}
}
/** 定义节点*/
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
/** 返回左子树的高度*/
public int leftHeight() {
if (left == null) {
return 0;
}
return left.height();
}
/** 返回右子树的高度*/
public int rightHeight() {
if (right == null) {
return 0;
}
return right.height();
}
/** 返回以该节点为根节点的树的高度*/
public int height() {
return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
}
/** 左旋转方法*/
private void leftRotate() {
// 1. 创建一个新的节点, 值等于当前根节点的值. (测试数据1的值4)
Node newNode = new Node(value);
// 2. 把新节点的左子树, 设置成当前节点的左子树
newNode.left = left;
// 3. 把新节点的右子树, 设置为当前节点的右子树的左子树
newNode.right = right.left;
// 4. 把当前节点的值换为右子节点的值
value = right.value; /** 将原来的根节点值4, 只将值改为6(测试数据1)*/
// 5. 把当前节点的右子树设置成当前节点的右子树的右子树
right = right.right;
/** 通过以上步骤3和5, 将原来的值6节点孤立抛弃(测试数据1)*/
// 6. 把当前节点的左子树设置为新节点
left = newNode;
}
/** 右旋转方法*/
private void rightRotate() {
// 创建一个新的节点, 值等于当前根节点的值. (测试数据2的值10)
Node newNode = new Node(value);
newNode.right = right;
newNode.left = left.right;
value = left.value;
left = left.left;
right = newNode;
}
/** 添加节点*/
public void add(Node node) {
if (node == null) {
return;
}
/** 小于当前节点, 加到右节点*/
if (node.value < this.value) {
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);
}
}
/** 添加完一个节点后, 如果 (右子树的高度-左子树的高度) > 1, 则左旋转*/
if(rightHeight() - leftHeight() > 1) {
/** 如果当前右子树的左子树的高度大于当前右子树的右子树的高度*/
if(right != null && right.leftHeight() > right.rightHeight()) {
/** 先对右子节点进行右旋转*/
right.rightRotate();
/** 再对当前节点进行左旋转*/
leftRotate();
} else {
/** 直接进行左旋转*/
leftRotate();
}
return;
}
/** 添加完一个节点后, 如果 (左子树的高度-右子树的高度) > 1, 则右旋转*/
if(leftHeight() - rightHeight() > 1) {
/** 如果当前左子树的右子树高度大于当前左子树的高度*/
if(left != null && left.rightHeight() > left.leftHeight()) {
/** 先对左子节点进行左旋转*/
left.leftRotate();
/** 再对当前节点进行右旋转*/
rightRotate();
} else {
/** 直接进行右旋转*/
rightRotate();
}
}
}
/** 中序遍历*/
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
@Override
public String toString() {
return "Node [value=" + value + "]";
}
}
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!