数据结构之自平衡二叉树(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,
自平衡二叉树中是否包含元素1true
判断生成的avl是否自平衡二叉树:true
判断生成的avl是否二分搜索树:true
AVLTree{root=null, size=0}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值