/** @author syh17 参考 https://blog.csdn.net/isunbin/article/details/81707606 */
public class AvlTree {
/**
* 获取树高
*
* @param root 获取树高的节点
* @return 树的高度
*/
public static int getHeight(Node root) {
if (root == null) {
return 0;
}
return root.height;
}
/**
* 更新树的高度
*
* @param root 需要更新树高的节点
*/
public static void updateHeight(Node root) {
root.height = 1 + Math.max(getHeight(root.left), getHeight(root.right));
}
/**
* ll型旋转
*
* @param root 需要旋转的节点
* @return 旋转后的节点
*/
public static Node llX(Node root) {
Node temp = root.left;
root.left = temp.right;
temp.right = root;
root.height = 1 + Math.max(getHeight(root.left), getHeight(root.right));
temp.height = 1 + Math.max(getHeight(temp.left), getHeight(temp.right));
return temp;
}
/**
* rr型旋转
*
* @param root 需要旋转的节点
* @return 旋转后的节点
*/
public static Node rrX(Node root) {
Node temp = root.right;
root.right = temp.left;
temp.left = root;
root.height = 1 + Math.max(getHeight(root.left), getHeight(root.right));
temp.height = 1 + Math.max(getHeight(temp.left), getHeight(temp.right));
return temp;
}
/**
* lr型旋转
*
* @param root 需要旋转的节点
* @return 旋转后的节点
*/
public static Node lrX(Node root) {
root.left = rrX(root.left);
return llX(root);
}
/**
* rl型旋转
*
* @param root 需要旋转的节点
* @return 旋转后的节点
*/
public static Node rlX(Node root) {
root.right = llX(root.right);
return rrX(root);
}
/**
* 向平衡二叉树插入数据
*
* @param root 插入二叉树的根节点
* @param key 插入的值
* @return 插入后的根节点
*/
public static Node insert(Node root, int key) {
// 如果节点是空 则直接新建一个节点返回
if (root == null) {
return new Node(key);
}
if (key < root.value) {
// 如果key小于当前节点, 则添加至其左子树
root.left = insert(root.left, key);
} else if (key > root.value) {
// 如果key大于当前节点, 则添加至其右子树
root.right = insert(root.right, key);
} else {
// key与当前值相等, 则直接返回此节点
return root;
}
// 更新树高
updateHeight(root);
// 获取两个子树的高度差
int difference = getDifference(root);
// 左子树过高且key小于左节点的值 说明需要ll旋转
if (difference > 1 && key < root.left.value) {
return llX(root);
}
// 右子树过高且key大于右节点的值 说明需要rr旋转
if (difference < -1 && key > root.right.value) {
return rrX(root);
}
// 左子树过高且key大于左节点的值 说明需要lr旋转
if (difference > 1 && key > root.left.value) {
return lrX(root);
}
// 右子树过高且key小于右节点的值 说明需要rl旋转
if (difference < -1 && key < root.right.value) {
return rlX(root);
}
return root;
}
/**
* 获取此树中的最大值
*
* @param root 根节点
* @return 当前树中的最大值
*/
public static int getMaxValue(Node root) {
Node current = root;
while (current.right != null) {
current = current.right;
}
return current.value;
}
/**
* 获取左子树高度与右子树高度的差值
*
* @param root 判断差值的根节点
* @return 左子树高度与右子树高度的差值
*/
public static int getDifference(Node root) {
return getHeight(root.left) - getHeight(root.right);
}
/**
* 删除节点
*
* @param root 需要删除节点的树的根节点
* @param key 需要删除的key
* @return 删除后的根节点
*/
public static Node deleteNode(Node root, int key) {
if (root == null) {
return null;
}
if (key < root.value) {
root.left = deleteNode(root.left, key);
} else if (key > root.value) {
root.right = deleteNode(root.right, key);
} else {
// 只有零个或一个子节点
if ((root.left == null) || (root.right == null)) {
// 如果有一个不为空 则可以获取到不为空的节点
Node temp = root.left == null ? root.right : root.left;
// 如果获取到了不为空的子节点, 则将子节点的值赋值给当前节点, 相当于删除这个子节点
if (temp != null) {
root.value = temp.value;
root.left = temp.left;
root.right = temp.right;
} else {
// 没有子节点, 说明是叶子节点 直接删除
return null;
}
} else {
// 如果两个子节点都存在, 取左子树的最大值作为root节点的值, 这样不会破坏平衡二叉搜索树的性质
root.value = getMaxValue(root.left);
// 删除左子树中的最大值, 因为左子树的最大值已经作为root的值了, 需要删除
root.left = deleteNode(root.left, root.value);
}
}
// 更新树高
updateHeight(root);
// 判断旋转
int difference = getDifference(root);
// 当前树的左子树高, 并且左子树的左子树高或者相等 LL型
if (difference > 1 && getDifference(root.left) >= 0) {
return llX(root);
}
// 当前树的左子树高, 并且左子树的右子树高 LR型
if (difference > 1 && getDifference(root.left) < 0) {
return lrX(root);
}
// 当前树的右子树高, 并且右子树的右子树高或者相等 RR型
if (difference < -1 && getDifference(root.right) <= 0) {
return rrX(root);
}
// 当前树的右子树高, 并且右子树的左子树高 Rl型
if (difference < -1 && getDifference(root.right) > 0) {
return rlX(root);
}
// 当前还是平衡的 不旋转
return root;
}
/**
* 中序遍历
*
* @param root 需要遍历的节点
*/
public static void zNode(Node root) {
if (root == null) {
return;
}
zNode(root.left);
System.out.println(root.value);
zNode(root.right);
}
/**
* 前序遍历
*
* @param root 需要遍历的节点
*/
public static void preNode(Node root) {
if (root == null) {
return;
}
System.out.println(root.value);
preNode(root.left);
preNode(root.right);
}
public static void main(String[] args) {
Node root = null;
root = insert(root, 2);
root = insert(root, 1);
root = insert(root, 0);
root = insert(root, 3);
root = insert(root, 4);
root = insert(root, 4);
root = insert(root, 5);
root = insert(root, 6);
root = insert(root, 9);
root = insert(root, 8);
root = insert(root, 7);
zNode(root);
deleteNode(root, 5);
System.out.println("-------------");
preNode(root);
System.out.println("-------------");
deleteNode(root, 4);
System.out.println("-------------");
preNode(root);
System.out.println("-------------");
zNode(root);
}
/** 树节点的定义 */
private static class Node {
private int value;
private Node left;
private Node right;
private int height;
public Node(int value) {
this.value = value;
this.height = 1;
}
}
}
Java-平衡二叉树-详细注释-无原理
最新推荐文章于 2023-09-30 21:08:44 发布