1、基本介绍
左旋转
右旋转
双旋转
2、应用实例
package tree;
/*
左旋转:
// 1、创建一个新节点,值为当前根节点的值
// 2、把根节点的左子树 设置 为新节点的左子树
// 3、把根节点的右子树的左子树 设置 为新节点的右子树
// 4、把根节点的值替换为其右子节点的值
// 5、把根节点的右子树 设置为 右子树的右子树
// 6、把根节点的左子树 设置为 新节点
右旋转:
// 1、创建一个新节点,值为当前根节点的值
// 2、把根节点的右子树 设置 为新节点的右子树
// 3、把根节点的左子树的右子树 设置 为新节点的左子树
// 4、把根节点的值替换为其左子节点的值
// 5、把根节点的左子树 设置为 左子树的左子树
// 6、把根节点的右子树 设置为 新节点
*/
public class AVLTreeDemo {
public static void main(String[] args) {
AVLTree tree1 = createAVLTree(new int[]{4,3,6,5,7,8}); // 测试左旋转
AVLTree tree2 = createAVLTree(new int[]{10,12,8,9,7,6}); // 测试右旋转
AVLTree tree3 = createAVLTree(new int[]{10,11,7,6,8,9}); // 测试双旋转
System.out.print("中序遍历:");
tree1.inOrderTraverse();
System.out.print("中序遍历:");
tree2.inOrderTraverse();
System.out.print("中序遍历:");
tree3.inOrderTraverse();
// 获取根节点所在的经左旋转后二叉排序树的高度、左子树高度以及右子树高度
System.out.println("\n当前根节点:" + tree1.root);
System.out.println("树的高度:" + tree1.root.height());
System.out.println("左子树的高度:" + tree1.root.leftHeight());
System.out.println("右子树的高度:" + tree1.root.rightHeight());
System.out.println();
// 获取根节点所在的经左旋转后二叉排序树的高度、左子树高度以及右子树高度
System.out.println("当前根节点:" + tree2.root);
System.out.println("树的高度:" + tree2.root.height());
System.out.println("左子树的高度:" + tree2.root.leftHeight());
System.out.println("右子树的高度:" + tree2.root.rightHeight());
System.out.println();
// 获取根节点所在的经左旋转后二叉排序树的高度、左子树高度以及右子树高度
System.out.println("当前根节点:" + tree3.root);
System.out.println("树的高度:" + tree3.root.height());
System.out.println("左子树的高度:" + tree3.root.leftHeight());
System.out.println("右子树的高度:" + tree3.root.rightHeight());
System.out.println();
}
public static AVLTree createAVLTree(int[] arr) {
AVLTree tree = new AVLTree();
for (int temp : arr) {
tree.add(new ANode(temp));
}
return tree;
}
}
class AVLTree {
public ANode root;
// 添加节点
public void add(ANode node) {
if (this.root == null) { // 判断当前是否是空树,是的话直接赋给root
this.root = node;
} else {
this.root.add(node);
}
}
// 中序遍历
public void inOrderTraverse() {
if (this.root != null) {
this.root.inOrderTraverse();
System.out.println();
}
}
//-----------------------------------------------------
// 删除节点
public void delANode(int data) {
if (this.root == null) {
return;
} else {
this.root.delANode(data);
}
}
// 查找待删节点
public ANode searchDelANode(int data) {
if (this.root == null) {
return null;
} else {
return this.root.searchDelANode(data);
}
}
// 查找待删节点的父结点
public ANode searchParentANode(int data) {
if (this.root == null) {
return null;
} else {
return this.root.searchParentANode(data);
}
}
public int searchRightMin(ANode ANode) {
if (this.root == null) {
return -1;
} else {
return this.root.searchRightMin(ANode);
}
}
public int searchLeftMax(ANode ANode) {
if (this.root == null) {
return -1;
} else {
return this.root.searchLeftMax(ANode);
}
}
}
class ANode {
public int data;
public ANode left;
public ANode right;
public ANode(int data) {
this.data = data;
this.left = null;
this.right = null;
}
// 添加节点
public void add(ANode node) {
if (node == null) {
return;
}
// 先判断大小,决定是放左子树还是右子树
if (node.data > this.data) {
// 要放右边的话,判断其右子树是否为空
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
// 要放左边的话,判断其左子树是否为空
} else {
if (this.left == null) {
this.left = node;
} else {
this.left.add(node);
}
}
// 在添加节点时,当左、右子树高度差大于1时,就要进行左右旋转
/*
如果右子树高度 - 左子树高度 > 1,则需要进行左旋转
注意:当符合左旋转条件时:
如果根节点的右子树下的左子树高度 大于 右子树高度,
就先要对该右子节点所在的右子树进行右旋转,再对根节点进行左旋转
不符合的话,可以直接进行左旋转
*/
if (rightHeight() - leftHeight() > 1) {
if (this.right != null && this.right.leftHeight() > this.right.rightHeight()) {
this.right.rightRotate();
leftRotate();
} else {
leftRotate();
}
return; // 旋转后就中止下面的旋转
}
/*
如果左子树高度 - 右子树高度 > 1,则需要进行右旋转
注意:当符合右旋转条件时:
如果根节点的左子树下的右子树高度 大于 左子树高度,
就先要对该左子节点所在的左子树进行左旋转,再对根节点进行右旋转
不符合的话,可以直接进行右旋转
*/
if (leftHeight() - rightHeight() > 1) {
if (this.left != null && this.left.rightHeight() > this.left.leftHeight()) {
this.left.leftRotate();
rightRotate();
} else {
rightRotate();
}
}
}
// 中序遍历
public void inOrderTraverse() {
if (this.left != null) {
this.left.inOrderTraverse();
}
System.out.print(this.data + " ");
if (this.right != null) {
this.right.inOrderTraverse();;
}
}
// 左旋转
public void leftRotate() {
// 1、创建一个新节点,值为当前根节点的值
ANode newNode = new ANode(this.data);
// 2、把根节点的左子树 设置 为新节点的左子树
newNode.left = this.left;
// 3、把根节点的右子树的左子树 设置 为新节点的右子树
newNode.right = this.right.left;
// 4、把根节点的值替换为其右子节点的值
this.data = this.right.data;
// 5、把根节点的右子树 设置为 右子树的右子树
this.right = this.right.right;
// 6、把根节点的左子树 设置为 新节点
this.left = newNode;
}
// 右旋转
public void rightRotate() {
// 1、创建一个新节点,值为当前根节点的值
ANode newNode = new ANode(this.data);
// 2、把根节点的右子树 设置 为新节点的右子树
newNode.right = this.right;
// 3、把根节点的左子树的右子树 设置 为新节点的左子树
newNode.left = this.left.right;
// 4、把根节点的值替换为其左子节点的值
this.data = this.left.data;
// 5、把根节点的左子树 设置为 左子树的左子树
this.left = this.left.left;
// 6、把根节点的右子树 设置为 新节点
this.right = newNode;
}
// 获取左子树的高度
public int leftHeight() {
if (this.left == null) {
return 0;
} else {
return this.left.height(); // 方法不要调错了,左子树它也是树
}
}
// 获取右子子树的高度
public int rightHeight() {
if (this.right == null) {
return 0;
} else {
return this.right.height();// 方法不要调错了,右子树它也是树
}
}
// 获取树的高度
public int height() {
// 取左、右子树高度最大值 + 1(根节点那一层)
return (Math.max(this.left == null ? 0 : this.left.height(),
this.right == null ? 0 : this.right.height()) + 1);
}
//-----------------------------------------------------
public void delANode(int data) {
ANode targetANode = searchDelANode(data);
ANode parentANode = searchParentANode(data);
if (targetANode == null || parentANode == null) {
return;
}
// 第一种情况:删除的是叶子节点
if (targetANode.left == null && targetANode.right == null) {
// 判断删除节点是父节点的左子节点还是右子结点,从而对应删除
if (parentANode.left == targetANode) {
parentANode.left = null;
} else if (parentANode.right == targetANode) {
parentANode.right = null;
}
// 第二种情况:删除的是只有一棵子树的非叶子节点
} else if ((targetANode.left == null && targetANode.right != null) ||
(targetANode.left != null && targetANode.right == null)) {
// 先判断删除节点有左子树还是右子树
// 有左子树
if (targetANode.left != null) {
// 再判断它是父节点的左子节点还是右子节点
if (targetANode == parentANode.left) {
parentANode.left = targetANode.left;
} else if (targetANode == parentANode.right) {
parentANode.right = targetANode.left;
}
// 右子树
} else if (targetANode.right != null) {
// 再判断它是父节点的左子节点还是右子节点
if (targetANode == parentANode.left) {
parentANode.left = targetANode.right;
} else if (targetANode == parentANode.right){
parentANode.right = targetANode.right;
}
}
// 第三种情况:删除的是有两棵子树的非叶子节点
} else if (targetANode.left != null && targetANode.right != null) {
// 无需判断删除节点是父节点的左子树还是右子树
// 只需找到删除节点的右子树的最小节点 或者 找到删除节点的左子树的最大节点
// int temp = searchRightMin(targetANode.right);
int temp = searchLeftMax(targetANode.left);
targetANode.data = temp;
}
}
public ANode searchDelANode(int data) {
if (this.data == data) {
return this;
} else if (data <= this.data) {
if (this.left != null) {
return this.left.searchDelANode(data);
}
} else {
if (this.right != null) {
return this.right.searchDelANode(data);
}
}
return null;
}
public ANode searchParentANode(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.searchParentANode(data);
}else if (this.right != null && data > this.data) {
return this.right.searchParentANode(data);
} else {
return null;
}
}
}
public int searchRightMin(ANode ANode) {
// 找该节点所在子树的最小值(根据二叉排序树特点,一般在最左边)
ANode temp = ANode;
while (temp.left != null) {
temp = temp.left;
}
delANode(temp.data); // 删除该最小子结点
return temp.data;
}
public int searchLeftMax(ANode ANode) {
// 找该节点所在子树的最大值(根据二叉排序树特点,一般在最右边)
ANode temp = ANode;
while (temp.right != null) {
temp = temp.right;
}
delANode(temp.data); // 删除该最小子结点
return temp.data;
}
@Override
public String toString() {
return "ANode[data=" + this.data + "]";
}
}