平衡二叉树及代码实战

平衡二叉树的插入

本文主要讲解平衡二叉树的节点插入时,怎么保证平衡,理解其旋转原理,对后续红黑树的学习也很重要

1.特性

对于树中任意一个节点,其左节点比右节点小,左子树高度与右子树高度差绝对值不能大于1。为此在插入元素时,AVL树使用左旋以及右旋来保证这个特性

2.旋转

AVL树插入时涉及四种旋转:右旋、左旋、先左后右、先右后左
2.1.右旋左子作父,父为右子,右孙变左孙
通俗讲:当前节点因左子树高不平衡后,需要拉升左子树,即将其左子升为父,自己做其右节点(当前节点肯定比其左子大,做它右子很自然),若这个左子原本有右子(对当前节点而言就叫右孙了),这时左子得右节点就重复了嘛,因右孙肯定比左子大,要在左子升父后得右边,而当前节点已作为右子,所以这个右孙就只能挂在当前节点得左节点上了,即从右孙变成了左孙了
右旋
2.2.左旋:右子作父,父为左子,左孙变右孙(与右旋互为镜像)
左旋

2.3. 先左后右(先左旋,使其满足右旋的形状,再右旋)
某些情况,直接左右旋转并不能使其平衡,我们需要调整到适用左旋或右旋的情况,如下
先左后右
2.4.先右后左(先右旋,使其满足左旋的形状,再左旋)
在这里插入图片描述

3.代码实战

1.AVLNode节点

package com.zg.demo.arithmetic.binarytree;

import lombok.Data;

/**
 * AVLNode class
 *
 * @author zg
 * @date 2021/3/22 17:33
 */
@Data
public class AVLNode {

    private int value;

    private int balance;

    private int depth;

    private AVLNode parentNode;

    private AVLNode leftNode;

    private AVLNode rightNode;

    public AVLNode(int value) {
        this.value = value;
        depth = 1;
        balance = 0;
    }


    public AVLNode(int value, AVLNode parentNode) {
        this.value = value;
        this.parentNode = parentNode;
        depth = 1;
        balance = 0;
    }

    public void insert(int value) {
        insert(this, value);
    }

    /**
     * 插入数据
     * @param root 根节点
     * @param value 插入值
     */
    private void insert(AVLNode root, int value) {
        if (value < root.value) {
            if (root.leftNode != null) {
                insert(root.leftNode, value);
            } else {
                root.leftNode = new AVLNode(value, root);
            }
        } else {
            if (root.rightNode != null) {
                insert(root.rightNode, value);
            } else {
                root.rightNode = new AVLNode(value, root);
            }
        }
        root.balance = calcBalance(root);
        // 左子树高,需要右旋
        if (root.balance > 1) {
            // 左子节点右子树高先左旋,旋转成符合右旋的结构
            if (root.leftNode.balance < 0) {
                leftRotate(root.leftNode);
            }
            rightRotate(root);
            return;
        }
        // 右子树高,需要左旋
        if (root.balance < -1) {
            // 右子节点左子树高先右旋,旋转成符合左旋的结构
            if (root.rightNode.balance > 0) {
                rightRotate(root.rightNode);
            }
            leftRotate(root);
            return;
        }
        root.balance = calcBalance(root);
        root.depth = calcDepth(root);
    }

    /**
     * 计算节点平衡因子,左右子节点都平衡则当前节点平衡
     * @param avlNode 节点
     * @return int
     */
    private int calcBalance(AVLNode avlNode) {
        int leftDepth = 0;
        int rightDepth = 0;
        if (avlNode.leftNode != null) {
            leftDepth = avlNode.leftNode.depth;
        }
        if (avlNode.rightNode != null) {
            rightDepth = avlNode.rightNode.depth;
        }
        return leftDepth - rightDepth;
    }

    /**
     * 计算节点高度,当前节点树高度
     * @param avlNode 节点
     * @return int
     */
    private int calcDepth(AVLNode avlNode) {
        int depth = 0;
        if (avlNode.leftNode != null) {
            depth = avlNode.leftNode.depth;
        }
        if (avlNode.rightNode != null && avlNode.rightNode.depth > depth) {
            depth = avlNode.rightNode.depth;
        }
        return ++depth;
    }

    /**
     * 右旋,因左子树高度比右子树高度大于1,破坏平衡二叉树结构
     * 因是左子树高了,需拉升左子树下节点,即:
     * 左子作父,父做右子,右孙变左孙
     * @param root 节点
     * 			   5							  3
     *          /    \                         /    \
     * 		   3	  8					     2		 5
     *       /   \                         /      /    \
     * 	   2	 4					     1		4	    8
     *   /
     * 1
     */
    private void rightRotate(AVLNode root) {
        // 左子
        AVLNode leftNode = root.leftNode;
        // 父
        AVLNode parentNode = root.parentNode;
        // 右孙
        AVLNode rightGrandSon = leftNode.rightNode;
        // 左子做父
        // 当前节点不是根节点,更改当前节点父节点引用
        if (parentNode != null) {
            // 判断当前节点在其父节点下是左节点还是右节点
            if (root == parentNode.leftNode) {
                parentNode.leftNode = leftNode;
            }
            if (root == parentNode.rightNode) {
                parentNode.rightNode = leftNode;
            }
        }
        leftNode.parentNode = parentNode;
        root.parentNode = leftNode;
        // 父做右子
        leftNode.rightNode = root;
        // 右孙变左孙,即左子做父后,其原本的右节点(右孙),需要转移到右子树做左孙,即做当前节点的左子节点
        root.leftNode = rightGrandSon;
        if (rightGrandSon != null) {
            rightGrandSon.parentNode = root;
        }
        // 重新计算高度和平衡因子
        root.depth = calcDepth(root);
        root.balance = calcBalance(root);
        leftNode.depth = calcDepth(leftNode);
        leftNode.balance = calcBalance(leftNode);
    }

    /**
     * 左旋 与右旋对称
     * 右子作父,父做左子,左孙变右孙
     * @param root 节点
     *        5                                 7
     *      /   \                           /      \
     *    3      7                         5        8
     *         /   \                      /  \       \
     *        6     8                   3     6       9
     *              \
     *               9
     */
    private void leftRotate(AVLNode root) {
        // 右子
        AVLNode rightNode = root.rightNode;
        // 父
        AVLNode parentNode = root.parentNode;
        // 左孙
        AVLNode leftGrandSon = rightNode.leftNode;
        if (parentNode != null) {
            if (parentNode.rightNode == root) {
                parentNode.rightNode = rightNode;
            }
            if (parentNode.leftNode == root) {
                parentNode.leftNode = rightNode;
            }
        }
        rightNode.parentNode = parentNode;
        root.parentNode = rightNode;
        rightNode.leftNode = root;
        root.rightNode = leftGrandSon;
        if (leftGrandSon != null) {
            leftGrandSon.parentNode = root;
        }

        root.depth = calcDepth(root);
        root.balance = calcBalance(root);
        rightNode.depth = calcDepth(rightNode);
        rightNode.balance = calcBalance(rightNode);
    }

    @Override
    public String toString() {
        return "AVLNode{" +
                "value=" + value +
                ", balance=" + balance +
                ", depth=" + depth +
                '}';
    }

}

2.Tree

package com.zg.demo.arithmetic.binarytree;

import lombok.Data;

/**
 * AVLTree class
 *
 * @author zg
 * @date 2021/4/1 10:25
 */
@Data
public class AVLTree {

    private AVLNode root;

    public void insertNode(int value) {
        if (root == null) {
            root = new AVLNode(value);
            return;
        }
        root.insert(value);
        setRootNode(root);
    }

    private void setRootNode(AVLNode node) {
        if (node.getParentNode() == null) {
            this.setRoot(node);
            return;
        }
        setRootNode(node.getParentNode());
    }
}

3.test

    public static void main(String[] args) {
        //平衡二叉树
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < 12; i++) {
            avlTree.insertNode(i);
        }
        // 层次打印方便查看
        printLevel(avlTree.getRoot());
    }
    
    private static void printLevel(AVLNode root) {
        LinkedBlockingQueue<AVLNode> queue = new LinkedBlockingQueue<>();
        queue.add(root);
        List<List<Integer>> lists = new ArrayList<>();
        lists.add(new ArrayList<>());
        while (!queue.isEmpty()) {
            AVLNode currentNode = queue.poll();
            int level = getLevel(currentNode, 0);
            if (lists.size() == level) {
                lists.add(new ArrayList<>());
            }
            List<Integer> list1 = lists.get(level);
            list1.add(currentNode.getValue());
            if (currentNode.getLeftNode() != null) {
                queue.add(currentNode.getLeftNode());
            }
            if (currentNode.getRightNode() != null) {
                queue.add(currentNode.getRightNode());
            }
        }
        lists.forEach(integers -> {
            integers.forEach(v -> {
                System.out.print(v);
                System.out.print("  ");
            });
            System.out.println();
        });
    }
    
    private static int getLevel(AVLNode node, Integer level) {
        if (node.getParentNode() != null) {
            return getLevel(node.getParentNode(), ++level);
        }
        return level;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值