平衡二叉搜索树,又被称为AVL树(Self-balancing binary search tree),且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
图解参考
其实就是基于BST树,区别就是添加节点和删除节点的时候判断树是否平衡,不平衡的时候,通过旋转的方式来让树平衡
/**
* AVL树的节点
*/
public class AVLTreeNode {
/**
* 节点的key值
*/
private int key;
/**
* 左节点
*/
private AVLTreeNode leftNode;
/**
* 右子节点
*/
private AVLTreeNode rightNode;
public AVLTreeNode() {
}
public AVLTreeNode(int key) {
this.key = key;
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public AVLTreeNode getLeftNode() {
return leftNode;
}
public void setLeftNode(AVLTreeNode leftNode) {
this.leftNode = leftNode;
}
public AVLTreeNode getRightNode() {
return rightNode;
}
public void setRightNode(AVLTreeNode rightNode) {
this.rightNode = rightNode;
}
@Override
public String toString() {
return "AVLTreeNode{" +
"key=" + key +
'}';
}
}
/**
* AVL树的节点
*/
public class AVLTree {
/**
* 树的根节点
*/
private AVLTreeNode root;
public AVLTreeNode getRoot() {
return root;
}
public void setRoot(AVLTreeNode root) {
this.root = root;
}
/**
* 添加树的节点
*
* @param node
*/
public void add(AVLTreeNode node) {
//跟节点不存在的情况下
if (root == null) {
root = node;
return;
}
//添加
addNode(root, node);
//添加完一个节点后,判断树是否平衡,不平衡就需要旋转
int leftHeight = getTreeHeight(root.getLeftNode());
int rightHeight = getTreeHeight(root.getRightNode());
//左节点多两个,右旋转
if (leftHeight - rightHeight > 1) {
//如果左子节点的右节点多了,那么就需要先旋转一次
//判断是都需要左旋转一次
//如果左子树的右子树的节点多,那么需要先左旋一次
if (getTreeHeight(root.getLeftNode().getRightNode()) > getTreeHeight(root.getLeftNode().getLeftNode())) {
leftRotation(getRoot().getLeftNode());
}
rightRotation(root);
} else if (rightHeight - leftHeight > 1) {
//判断是否需要右旋转一次
if (getTreeHeight(root.getRightNode().getRightNode()) < getTreeHeight(root.getRightNode().getLeftNode())) {
rightRotation(getRoot().getRightNode());
}
//左旋转
leftRotation(root);
}
}
/**
* 添加节点
*
* @param target 待比较的节点
* @param node 待添加节点
*/
private void addNode(AVLTreeNode target, AVLTreeNode node) {
//小于就添加到左子树,大于就到右子树,等于就等于重复,不处理
int targetKey = target.getKey();
int key = node.getKey();
if (targetKey == key) {
System.out.println("当前节点已存在!");
return;
}
if (key < targetKey) {
if (target.getLeftNode() == null) {
target.setLeftNode(node);
} else {
addNode(target.getLeftNode(), node);
}
} else {
if (target.getRightNode() == null) {
target.setRightNode(node);
} else {
addNode(target.getRightNode(), node);
}
}
}
/**
* 查找
*
* @param key
* @return
*/
public AVLTreeNode search(int key) {
if (root == null) {
return null;
}
return searchNode(root, key);
}
/**
* 搜索节点
*
* @param target 待搜索节点
* @param key
* @return
*/
private AVLTreeNode searchNode(AVLTreeNode target, int key) {
if (target == null) {
return null;
}
int targetKey = target.getKey();
if (targetKey == key) {
return target;
}
if (key < targetKey) {
return searchNode(target.getLeftNode(), key);
} else {
return searchNode(target.getRightNode(), key);
}
}
/**
* 中序遍历
*/
public void midTraverse() {
if (root == null) {
return;
}
midTraverse(root);
}
/**
* 遍历
*
* @param node
*/
private void midTraverse(AVLTreeNode node) {
if (node.getLeftNode() != null) {
midTraverse(node.getLeftNode());
}
System.out.println(node);
if (node.getRightNode() != null) {
midTraverse(node.getRightNode());
}
}
/**
* 删除节点
*
* @param key
*/
public void delete(int key) {
//删除节点需要先找到待删除节点的父节点
AVLTreeNode parentNode = searchParentNode(root, key);
if (parentNode == null) {
System.out.println("未找到待删除节点的父节点!");
return;
}
System.out.println("parentNode=" + parentNode);
//找到待删除的节点
AVLTreeNode target = (parentNode.getLeftNode() != null && parentNode.getLeftNode().getKey() == key) ?
parentNode.getLeftNode() : parentNode.getRightNode();
//删除分为三种情况
//1.删除叶子节点
//2.删除有一个子节点的节点
//3.删除有两个子节点的子节点
int childNum = getChildNum(target);
if (childNum != 2) {
deleteChildNode(parentNode, target);
} else {
//有两个子节点,那么就需要做一次替换,然后删除
//找到右子树的最小的节点,删除该节点,然后把值替换待删除节点
//为什么是右子树的最小值呢,因为右子树的值都大于当前节点和左子树,选最小是因为右子树的值都要大于父节点
AVLTreeNode minNode = getMinRightNode(target.getRightNode());
//删除该节点
delete(minNode.getKey());
//移动节点
target.setKey(minNode.getKey());
}
//删除完毕后判断旋转
}
/**
* @param node
* @return
*/
public AVLTreeNode getMinRightNode(AVLTreeNode node) {
AVLTreeNode temp = node;
while (temp.getLeftNode() != null) {
temp = temp.getLeftNode();
}
return temp;
}
/**
* 删除指定的子节点
*
* @param parentNode
* @param target
*/
private void deleteChildNode(AVLTreeNode parentNode, AVLTreeNode target) {
//判断是哪个节点,找到后,直接指向待删除节点的下一个节点
//如果子节点没有子节点,那么也是直接变成null
if (parentNode.getLeftNode() == target) {
parentNode.setLeftNode(target.getLeftNode());
} else {
parentNode.setRightNode(target.getRightNode());
}
}
/**
* 搜索父节点
*
* @param target 待搜索节点
* @param key
* @return
*/
private AVLTreeNode searchParentNode(AVLTreeNode target, int key) {
if (target == null) {
return null;
}
//判断当前节点是否就是要找的父节点
//匹配任何一个子节点,那么就算找到
if ((target.getLeftNode() != null && target.getLeftNode().getKey() == key)
|| (target.getRightNode() != null && target.getRightNode().getKey() == key)) {
return target;
}
if (key < target.getKey()) {
return searchParentNode(target.getLeftNode(), key);
} else {
return searchParentNode(target.getRightNode(), key);
}
}
/**
* 该节点左右子节点的数量
* 叶子节点返回0
* 有左子节点/右子节点中的一个返回1
* 同时存在左右子节点 返回2
*
* @param node
* @return
*/
private int getChildNum(AVLTreeNode node) {
int num = 0;
if (node.getLeftNode() != null) {
num++;
}
if (node.getRightNode() != null) {
num++;
}
return num;
}
/**
* 获取以该节点为父节点的子树的高度
*
* @return
*/
public int getTreeHeight(AVLTreeNode node) {
if (node == null) {
return 0;
}
return Math.max(node.getLeftNode() == null ? 0 : getTreeHeight(node.getLeftNode()),
node.getRightNode() == null ? 0 : getTreeHeight(node.getRightNode())) + 1;
}
/**
* 右边节点多两个的时候,进行左旋转
*
* @param node
*/
public void leftRotation(AVLTreeNode node) {
//创建新的节点
AVLTreeNode newNode = new AVLTreeNode(node.getKey());
//左节点指向node的左节点
newNode.setLeftNode(node.getLeftNode());
//右子节点指向node的右子节点的左节点
newNode.setRightNode(node.getRightNode().getLeftNode());
//node的左子节点指向newNode
node.setLeftNode(newNode);
//node的值变为右子节点的值
node.setKey(node.getRightNode().getKey());
//把右子节点删除掉
node.setRightNode(node.getRightNode().getRightNode());
}
/**
* 左边节点多两个的时候,进行右旋转
*
* @param node
*/
public void rightRotation(AVLTreeNode node) {
//创建新的节点
AVLTreeNode newNode = new AVLTreeNode(node.getKey());
//右节点指向node节点的右节点
newNode.setRightNode(node.getRightNode());
//左子节点指向node节点的左子节点的右子及诶单
newNode.setLeftNode(node.getLeftNode().getRightNode());
//node的右子节点指向newNode
node.setRightNode(newNode);
//node的值变为右子节点的值
node.setKey(node.getLeftNode().getKey());
//把左子节点删除掉
node.setLeftNode(node.getLeftNode().getLeftNode());
}
}
/**
* 测试
*/
public class TestClass {
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
// int[] array = new int[]{4,3,6,5,7,8};
//{10,12, 8, 9, 7, 6}
// int[] array = new int[]{10, 12, 8, 9, 7, 6};
int[] array = { 10, 11, 7, 6, 8, 9 };
AVLTree tree = new AVLTree();
for (int i = 0; i < array.length; i++) {
int key = array[i];
AVLTreeNode node = new AVLTreeNode(key);
tree.add(node);
}
System.out.println("中序遍历");
tree.midTraverse();
System.out.println("树的高度" + tree.getTreeHeight(tree.getRoot()));
System.out.println("左子树的高度" + tree.getTreeHeight(tree.getRoot().getLeftNode()));
System.out.println("右子树的高度" + tree.getTreeHeight(tree.getRoot().getRightNode()));
}
}