图示AVL平衡二叉树的左旋、右旋以及树高

一.下图为左旋转示例:

  • 首先,当二叉排序树添加了最后一个节点8的时候,不满足AVL树的条件,因为此时:
  • 树的整体高度为4,右子树高度为3,左子树的高度为1,3-1>1,所以需要左旋了
  • 直接对该树root=4,进行左旋即可
root根节点左旋条件为:右子树len - 左子树len > 1;
root根节点右旋条件为:左子树len - 右子树len > 1;

在这里插入图片描述

二.下图为右旋同理:

在这里插入图片描述

三.下图为右旋前先左旋转示例:

在这里插入图片描述

四.同理左旋前判断是否需要先右旋:

/**
 * AVL树
 *      1.先BST树添加节点
 *          是否需要左旋
 *                  是否先右旋
 *          是否需要右旋
 *                  是否先左旋
 */
public class Main33 {

    private static void avl(Node root, Node temp, int ele) {
        //1.二叉搜索树的创建,完全和二叉排序数新增节点一样
        if (temp.val > ele){
            if (temp.left == null){
                temp.left = new Node(ele);
            }else {
                avl(root, temp.left,ele );//看看应该放在左子节点的左边还是右边
            }
        }else {
            if (temp.right == null){
                temp.right = new Node(ele);
            }else {
                avl(root, temp.right,ele );
            }
        }
        //2.二叉排序树添加完节点后,判断是否需要旋转,转换成AVL树
        // 需要右旋转才能变成AVL树:左子树的高度比右子树的高度差大于1
        if ((root.left !=null && root.right != null) && (treelen(root.left) - treelen(root.right) > 1) ){
             //3.在确定需要右旋转的时候,需要注意的是:先判断根节点的左子树的 右子树 > 左子树的左子树 ,大于的话,需要先对左子树进行左旋转
            //左子树的右子树 > 左子树的左子树,先对左子树进行左旋转
            if ((root.left.right !=null && root.left.left != null) && treelen(root.left.right) > treelen(root.left.left)) {
                //4.左子树进行左旋转
                leftspin(root.left);
            }else {
                //5.次树,进行正常右旋
                rightspin(root);
            }
            return;//必须,一旦BST添加完一个节点,然后通过右旋后此节点已经满足AVL树的条件了,就不用走下面的左旋!!!
        }
        //如果已右旋转,则return结束了,不用再下面的左旋转
        //同理右旋
        if ((root.left !=null && root.right != null) && treelen(root.right) - treelen(root.left) > 1){
            if ((root.right.left !=null && root.right.right != null) && treelen(root.right.left) > treelen(root.right.right)){
                rightspin(root.right);
            }else {
                leftspin(root);
            }
        }

    }

    /**
     * 右旋
     */
    private static void rightspin(Node root) {
        //1.创建新节点
        Node node = new Node(root.val);
        //2.node的左右子节点
        node.right = root.right;
        node.left = root.left.right;
        //3.root值被覆盖
        root.val = root.left.val;
        //4.root的左右子节点
        root.right = node;
        root.left = root.left.left;
    }

    /**
     * 左旋
     */
    private static void leftspin(Node root) {
        //1.创建新节点
        Node node = new Node(root.val);
        //2.node的左右子节点
        node.left = root.left;
        node.right = root.right.left;
        //3.root覆盖新值
        root.val = root.right.val;
        //4.root的左右子节点
        root.left = node;
        root.right = root.right.right;
    }

    /**
     * 以node为根节点的树的高度
     * @param node
     * @return
     */
    private static int treelen(Node node) {
        return Math.max(node.left == null ? 0 : treelen(node.left), node.right == null ? 0 : treelen(node.right)) + 1;
    }

    /**
     * 前序遍历AVL树
     * @param root
     */
    private static void pre(Node root) {
        if (root == null)
            return;
        System.out.println(root.val);
        pre(root.left);
        pre(root.right);
    }

    public static void main(String[] args) {
        //int[] array = {4,3,6,5,7,8};//左旋
        //int[] array = {6,3,7,1,4,0};//右旋
        int[] array = {10,11,7,6,8,9};//双旋转(此处是,先对根节点左子树进行左旋,再对树进行右旋)
        Node root = new Node(array[0]);
        Node temp = root;
        for (int i = 1; i < array.length; i++) {
            avl(root,temp,array[i]);//!!!这里注意的是,为了确定树根节点的高度,以及根节点的左右字树的高度,需要用一个temp变量代替root去执行添加节点操作
            //这样,root和temp为堆上同一块内存,temp添加了节点,root可以看到变化,这样,为了能够以root查树高
        }
        pre(root);//前序遍历
    }
}


class Node{
    public int val;
    public Node left;
    public Node right;
    public Node(int val){
        this.val = val;
    }
}

猜你喜欢

转载自blog.csdn.net/tmax52HZ/article/details/108033300