自平衡二叉树(AVL)
自平衡二叉树特点
- 特点:任意一个结点,左右子树的高度差不能超过1。
旋转:加入结点后,需要沿着结点向上维护平衡性。
1、插入结点在不平衡结点的左侧的左侧(即左子树的左子树上),需要右旋。
2、插入结点在不平衡结点的右侧的右侧(即右子树的右子树上),需要左旋。
3、插入结点在不平衡结点的左侧的右侧(即左子树的右子树上),需要先对左子树进行左旋,再对整体进行右旋。
4、插入结点在不平衡结点的右侧的右侧(即右子树的右子树上),需要先对右子树进行右旋,再对整体进行左旋。
自定义平衡二叉树结点
package com.company.AVLTree;
/**
* 自定义AVl树(自平衡二叉树)结点
* 为了确保数据的可比性,所以泛型需要继承Comparable类
*
* @param <T>
* @Author: wenhua
* @CreateTime: 2023-01-14 17:01
*/
public class Node<T extends Comparable<T>> {
T ele;// 节点数据
Node left;
Node right;// 左孩子和右孩子
int height;// 以该结点为根的二分搜索树高度
public Node(){
this(null);
}
public Node(T ele){
this.ele = ele;
left = null;
right = null;
this.height = 1;
}
@Override
public String toString() {
return "Node{" +
"ele=" + ele +
", left=" + left +
", right=" + right +
", height=" + height +
'}';
}
}
自定义平衡二叉树
package com.company.AVLTree;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义AVl树(自平衡二叉树)
*
* @param <T>
* @Author: wenhua
* @CreateTime: 2023-01-14 17:10
*/
public class AVLTree<T extends Comparable> {
private Node root;
private int size;
public AVLTree() {
root = null;
size = 0;
}
/**
* 判断AVL树是否为空
*
* @return 返回布尔值
*/
public boolean isEmpty() {
return size == 0;
}
/**
* AVL树的长度
*
* @return 返回int值
*/
public int getSize() {
return this.size;
}
/**
* 获取结点的高度
*
* @param node 根结点
* @return 返回int值
*/
public int getNodeHeight(Node node) {
if (node == null) {
return 0;
}
return node.height;
}
/**
* 获取结点的平衡因子(左子树的高度减去右子树的高度)
*
* @param node 根结点
* @return 返回int值
*/
public int getBalance(Node node) {
if (node == null) {
return 0;
}
return getNodeHeight(node.left) - getNodeHeight(node.right);
}
/**
* 判断其是否为二叉搜索树
*
* @return 返回布尔值
*/
public boolean isSearchTree() {
return isSearchTree(root);
}
/**
* 判断其是否为二叉搜索树
*
* @param node 根结点
* @return 返回布尔值
*/
private boolean isSearchTree(Node node) {
// 判断根结点是否为空
if (node == null) {
return true;
}
// 创建List对象,用于存储数据
List<T> ele = new ArrayList<>();
midTraver(node, ele);
// 遍历List对象中的数据,一旦乱序则返回false
for (int i = 1; i < ele.size(); i++) {
if (ele.get(i - 1).compareTo(ele.get(i)) > 0) {
return false;
}
}
return true;
}
/**
* 中序遍历二叉树(二叉搜索树性质:中序遍历是有序的)
*
* @param node 根节点
* @param list 存储结点元素
*/
private void midTraver(Node node, List list) {
// 递归到底的情况
if (node == null) {
return;
}
midTraver(node.left,list);
list.add(node.ele);
midTraver(node.right,list);
}
/**
* 判断该二叉树是否为平衡二叉树
*
* @return 返回布尔值
*/
public boolean isBalanceTree() {
return isBalanceTree(root);
}
/**
* 递归判断是否二叉树是否为平衡二叉树
*
* @param node 根结点
* @return 返回布尔值
*/
private boolean isBalanceTree(Node node) {
if (node == null) {
return true;
}
int balance = Math.abs(getBalance(node));
if (balance > 1) {
return false;
}
return isBalanceTree(node.left) && isBalanceTree(node.right);
}
/**
* 添加AVL树结点
*
* @param ele 添加元素
*/
public void put(T ele) {
root = put(root, ele);
}
/**
* 递归添加二分搜索树结点
*
* @param head 根结点
* @param ele 添加元素
* @return 返回添加后的根结点
*/
private Node put(Node head, T ele) {
// 递归结束条件
if (head == null) {
Node node = new Node(ele);
size++;
return node;
}
// 变成重复的同一问题
if (head.ele.compareTo(ele) > 0) {
head.left = put(head.left, ele);
} else if (head.ele.compareTo(ele) < 0) {
head.right = put(head.right, ele);
}
// 添加完结点后, 维护平衡
// getBalance是左子树高度减去右子树高度,那么大于1,则说明左子树高度高,左子树右转
if (getBalance(head) > 1 && getBalance(head.left) >= 0) {
head = RightRever(head);
}
// 小于-1,则说明右子树高度高,即右子树左转
else if (getBalance(head) < -1 && getBalance(head.right) <= 0) {
head = LeftRever(head);
// 即以head为根结点的左子树不平衡,但是以head右子树为根时,左子树不平衡,即左子树的右子树不平衡
} else if (getBalance(head) > 1 && getBalance(head.left) < 0) {
// 先对不平衡结点的左子树进行右旋
head.left = RightRever(head.left);
// 再对当前结点整体左旋
head = LeftRever(head);
}
// 即以head为根结点的右子树不平衡,但是以head右子树为根时,左子树不平衡,即右子树的左子树不平衡
else if (getBalance(head) < 1 && getBalance(head.right) > 0) {
// 先对不平衡结点的右子树进行左旋
head.right = LeftRever(head.right);
// 再对当前结点整体右旋
head = RightRever(head);
}
// 对当前结点的高度进行维护,获取左右子树中较高高度,加1即可
head.height = Math.max(getNodeHeight(head.left), getNodeHeight(head.right)) + 1;
return head;// 和节点元素相等时
}
/**
* 当以node为树根结点的左子树不平衡时(右转)
*
* @param node 根结点
* @return 返回根结点
*/
public Node RightRever(Node node) {
Node leftNode = node.left;
Node leftNodeRight = leftNode.right;
leftNode.right = node;
node.left = leftNodeRight;
node.height = 1 + Math.max(getNodeHeight(node.left), getNodeHeight(node.right));
leftNode.height = 1 + Math.max(getNodeHeight(leftNode.left), getNodeHeight(leftNode.right));
return leftNode;
}
/**
* 当以node为树根结点的右子树不平衡时(左转)
*
* @param node 根结点
* @return 返回根结点
*/
public Node LeftRever(Node node) {
Node rightNode = node.right;
Node rightNodeLeft = rightNode.left;
rightNode.left = node;
node.right = rightNodeLeft;
node.height = 1 + Math.max(getNodeHeight(node.left), getNodeHeight(node.right));
rightNode.height = 1 + Math.max(getNodeHeight(rightNode.left), getNodeHeight(rightNode.right));
return rightNode;
}
/**
* 判断该结点是否存在
*
* @param ele 指定元素
* @return 返回布尔值
*/
public boolean isContain(T ele) {
return isContain(root, ele);
}
/**
* 递归判断该结点是否存在于二分搜索树中
* 即就是查找该结点
*
* @param node 根结点
* @param ele 指定元素
* @return 返回布尔值
*/
private boolean isContain(Node node, T ele) {
// 最糟糕情况,即遍历完二分搜索树该结点不存在
if (node == null) {
return false;
}
if (node.ele.compareTo(ele) > 0) {// 当前节点数据大于ele,则在左子树中查找
return isContain(node.left, ele);
} else if (node.ele.compareTo(ele) < 0) { // 当前节点数据小于ele,则在右子树中查找
return isContain(node.right, ele);
}
return true;
}
/**
* 移除指定结点
* 1、判断指定结点是否存在
* 2、找到指定删除的结点(递归,删除后的子树需要返回)
* 3、判断其左右子树情况
* 3.1只有右子树或只有左子树时,直接换为子树结点
* 3.2无子树时直接删除
* 3.3左右子树同时存在,找到右子树最小结点与之替换
*
* @param ele 移除元素
*/
public void remove(T ele) {
root = remove(root, ele);
}
/**
* 移除指定元素
*
* @param node 根结点
* @param ele 移除元素
* @return 返回根结点
*/
private Node remove(Node node, T ele) {
// 如果当前结点为空,则不存在该结点,返回空
if (node == null) {
return null;
}
Node result = null;
// 查找指定元素的结点
if (node.ele.compareTo(ele) > 0) {// 当前结点数据大于查找结点数据,向左子树查找
// 将删除指定元素结点后的树结构赋值给当前结点的左子树
node.left = remove(node.left, ele);
// 返回当前结点,与前面结点连接
result = node;
} else if (node.ele.compareTo(ele) < 0) {// 当前结点数据小于查找结点数据,向右子树查找
// 将删除指定元素结点后的树结构赋值给当前结点的右子树
node.right = remove(node.right, ele);
// 返回当前结点,与前面结点连接
result = node;
} else {// 找到指定元素的结点
if (node.left == null) {// 左子树为空时,直接将右子树赋予当前要删除的结点
// 将当前结点的右子树赋值给临时结点变量
Node rightNode = node.right;
// 树的结点数量减1
size--;
// 将当前结点的右子树置空
node.right = null;
// 返回临时结点变量,与其他结点连接
result = rightNode;
} else if (node.right == null) {// 右子树为空时,直接将左子树赋予当前要删除的结点
Node leftNode = node.left;
// 树的结点数量减1
size--;
// 将当前结点的左子树置空
node.left = null;
// 返回临时结点变量,与其他结点连接
result = leftNode;
} else {
// 以当前结点为根,找到当前结点右子树中最小的结点
Node tempNode = getMinNode(node.right);
// 以当前结点为根,删除右子树中的最小结点,并返回删除后的根结点
Node delNode = removeMinNode(node.right);
// 树的结点数量减1
size--;
// 将右子树中的最小结点删除后的树结构赋值给最小结点的右子树
tempNode.right = delNode;
// 当前结点的左子树赋值给最小结点的左子树
tempNode.left = node.left;
// 将当前结点的左右子树置空
node.left = node.right = null;
// 返回临时结点变量,与其他结点连接
result = tempNode;
}
}
if (result == null) {
return null;
}
// 添加完结点后, 维护平衡
// getBalance是左子树高度减去右子树高度,那么大于1,则说明左子树高度高,左子树右转
if (getBalance(result) > 1 && getBalance(result.left) >= 0) {
result = RightRever(result);
}
// 小于-1,则说明右子树高度高,即右子树左转
else if (getBalance(result) < -1 && getBalance(result.right) <= 0) {
result = LeftRever(result);
} else if (getBalance(result) > 1 && getBalance(result.left) < 0) {
result.left = RightRever(result.left);
result = LeftRever(result);
}
// 即以head为根结点的右子树不平衡,但是以head右子树为根时,左子树不平衡,即右左子树不平衡
else if (getBalance(result) < 1 && getBalance(result.right) > 0) {
// 先对head的右子树进行左转
result.right = LeftRever(result.right);
result = RightRever(result);
}
result.height = Math.max(getNodeHeight(result.left), getNodeHeight(result.right)) + 1;
return result;// 和节点元素相等时
}
/**
* 根据二分搜索树的性质获取最小结点数据
*
* @return 返回最小元素
*/
public T getMinEle() {
if (isEmpty())
return null;
Node temp = root;
while (temp.left != null) {
temp = temp.left;
}
return (T) temp.ele;
}
/**
* 通过递归获得二分搜索树的最小结点数据
*
* @return 返回最小元素
*/
public T getMinElement() {
if (isEmpty())
return null;
return (T) getMinNode(root).ele;
}
private Node getMinNode(Node node) {
if (node.left == null)
return node;
return getMinNode(node.left);
}
/**
* 移除最小结点
*
* @return 返回最小元素
*/
public T removeMinNode() {
if (isEmpty())
return null;
T ele = getMinEle();
root = removeMinNode(root);
return ele;
}
/**
* 删除以node为根结点的最小结点
*
* @param node 根结点
* @return 返回最小结点
*/
private Node removeMinNode(Node node) {
if (node.left == null) {
Node rightNode = node.right;
size--;
node.right = null;
return rightNode;
}
node.left = removeMinNode(node.left);
return node;
}
/**
* 递归层序遍历获取AVL所有节点的元素
*
* @param node 以当前结点为根结点
* @param list 用于存储结点元素
*/
private void getList(Node node, List<T> list) {
// 定义队列
Queue queue = new LinkedList();
// 入队列
queue.offer(root);
// 循环判断队列是否为空,如果为空则遍历完成
while (!queue.isEmpty()) {
// 队列中元素出列
Node temp = (Node) queue.poll();
list.add((T) temp.ele);
// 判断当前结点左子树是否为空,不为空,则入队等待读取
if (temp.left != null) {
queue.offer(temp.left);
}
// 判断当前结点右子树是否为空,不为空,则入队等待读取
if (temp.right != null) {
queue.offer(temp.right);
}
}
}
/**
* 便于外界获取二叉树所有节点的元素
*
* @param list 用于存储结点元素
*/
public void getList(List<T> list) {
getList(root, list);
}
@Override
public String toString() {
return "AVLTree{" +
"root=" + root +
", size=" + size +
'}';
}
}
测试
package com.company.AVLTree;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: wenhua
* @CreateTime: 2023-01-14 17:01
*/
public class Main {
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7,8,9,10,11,12};
// 创建AVL树对象
AVLTree avlTree = new AVLTree();
for ( int i = 0; i< array.length ; i++){
avlTree.put(array[i]);
}
System.out.println(avlTree);// 使用重写toString方法
// 接收AVL树中的数据,层序遍历
List list = new ArrayList();
avlTree.getList(list);
System.out.print("层序遍历AVL树结构:");
list.stream().forEach(item->System.out.print(item+","));
System.out.println();
System.out.println("自平衡二叉树中是否包含元素1:"+avlTree.isContain(1));
System.out.println("判断生成的avl是否自平衡二叉树:"+avlTree.isBalanceTree());
System.out.println("判断生成的avl是否二分搜索树:"+avlTree.isSearchTree());
// for循环依次移除对应元素
for (int i = 0 ; i < array.length; i++){
avlTree.remove(array[i]);
}
System.out.println(avlTree);
}
}
添加完数据后,AVL树如下图所示:
测试结果
AVLTree{root=Node{ele=8, left=Node{ele=4, left=Node{ele=2, left=Node{ele=1, left=null, right=null, height=1}, right=Node{ele=3, left=null, right=null, height=1}, height=2}, right=Node{ele=6, left=Node{ele=5, left=null, right=null, height=1}, right=Node{ele=7, left=null, right=null, height=1}, height=2}, height=3}, right=Node{ele=10, left=Node{ele=9, left=null, right=null, height=1}, right=Node{ele=11, left=null, right=Node{ele=12, left=null, right=null, height=1}, height=2}, height=3}, height=4}, size=12}
层序遍历AVL树结构:8,4,10,2,6,9,11,1,3,5,7,12,
自平衡二叉树中是否包含元素1:true
判断生成的avl是否自平衡二叉树:true
判断生成的avl是否二分搜索树:true
AVLTree{root=null, size=0}