Balanced binary tree (AVL) (Java implementation)

Balanced binary tree (AVL) (Java implementation)

1. Problems in binary search tree

Although binary tree search has some advantages in query, deletion and addition, its efficiency is also very low in some cases. For example, the following binary search tree:

We find that its right subtree is very high, which makes it lose the function of efficient search that binary tree originally has. Its search efficiency is even lower than that of linked list, because it needs to judge whether it has left subtree or right subtree when traversing a node. In order to avoid this situation, a balanced binary tree is introduced.

2. Definition of balanced binary tree (AVL)

First, the balanced binary tree is a special binary search tree. Secondly, the absolute values of height of left and right subtrees of balanced binary trees are less than 1

3. Find the height of binary tree

Recursion, it's not easy to understand the code directly. It's easy to understand when drawing.

/**
     * Height of binary tree
     * @return
     */
    public int height(){
        return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
    }

Understand the above code, similarly, the height of the left and right subtrees is also very good.

/**
     * Height of left subtree
     * @return
     */
    public int leftHeight(){
        if (left==null){
            return 0;
        }
        return left.height();
    }

    /**
     * Height of right subtree
     * @return
     */
    public int rightHeight(){
        if (right==null){
            return 0;
        }
        return right.height();
    }

Let's test the height of the binary tree, the height of the left subtree and the height of the right subtree

public class AVLTreeDemo {
    public static void main(String[] args) {
        int[] arr={4,3,6,5,7,8};
        //Create an AVLTree object
        AVLTree avlTree = new AVLTree();
        //Add node
        for (int i = 0; i < arr.length; i++) {
            avlTree.add(new Node(arr[i]));
        }
        System.out.println("Sequential traversal");
        avlTree.infixOrder();
        System.out.println("Before balancing");
        System.out.println("Height of tree:"+avlTree.getRoot().height());
        System.out.println("Height of left subtree:"+avlTree.getRoot().leftHeight());
        System.out.println("Height of right subtree:"+avlTree.getRoot().rightHeight());
    }
}

The output is as follows, exactly the same as the picture we see:

4. The left rotation of BST forms AVL

Conditions for left rotation: when a node is added, the height of the right subtree - the height of the left subtree > 1, the left rotation occurs.

/**
     * Left rotation method
     */
    public void leftRotate(){
        //Create a new node with the value of the current root node
        Node newNode = new Node(this.value);
        //Set the new node's left subtree to the current node's left subtree
        newNode.left=this.left;
        //Set the right subtree of the new node to the left subtree of the right subtree of the current node
        newNode.right=this.right.left;
        //Replace the value of the current node with the value of the right child node
        this.value=this.right.value;
        //Set the right subtree of the current node to the right subtree of the right subtree of the current node
        this.right=this.right.right;
        //Set the left child of the current node as a new node
        this.left=newNode;
    }
	 //How to add nodes
    public void add(Node node){
        if (node==null){
            return;
        }
        //Determine the relationship between the value of the incoming node and the value of the node of the current subtree
        if (node.value<this.value){
            //If the left child of the current node is empty
            if (this.left==null){
                this.left=node;
            }else {
                //Recursively add to left subtree
                this.left.add(node);
            }
        }else {//The value of the added node is greater than the value of the current node
            if (this.right==null){
                this.right=node;
            }else {//Recursively add to right subtree
                this.right.add(node);
            }
        }
        //When a node is added, the height of the right subtree - the height of the left subtree is more than 1, and the left rotation occurs
        if (rightHeight()-leftHeight()>1){
       		leftRotate();//Left rotation
        }
    }

Continuing to test the height of a binary tree, we find that BTS has self balanced into AVL. After left rotation, the original binary tree becomes like this:

5. Right rotation of BST to form AVL

Conditions for right rotation: when a node is added, the height of the left subtree - the height of the right subtree is more than 1, and right rotation occurs.

/**
     * Right rotation method
     */
    public void rightRotate(){
        //Create a new node with the value of the current root node
        Node newNode = new Node(this.value);
        //Set the right subtree of the new node as the right subtree of the current node
        newNode.right=this.right;
        //Set the left subtree of the new node as the right subtree of the left subtree of the current node
        newNode.left=this.left.right;
        //Replace the value of the current node with the value of the left child node
        this.value=this.left.value;
        //Set the left subtree of the current node to the left subtree of the left subtree of the current node
        this.left=this.left.left;
        //Set the right child of the current node as a new node
        this.right=newNode;
    }
    //How to add nodes
    public void add(Node node){
        if (node==null){
            return;
        }
        //Determine the relationship between the value of the incoming node and the value of the node of the current subtree
        if (node.value<this.value){
            //If the left child of the current node is empty
            if (this.left==null){
                this.left=node;
            }else {
                //Recursively add to left subtree
                this.left.add(node);
            }
        }else {//The value of the added node is greater than the value of the current node
            if (this.right==null){
                this.right=node;
            }else {//Recursively add to right subtree
                this.right.add(node);
            }
        }
        //When a node is added, the height of the right subtree - the height of the left subtree is more than 1, and the left rotation occurs
        if (rightHeight()-leftHeight()>1){
           /* if (this.right!=null&&this.right.rightHeight()<this.right.leftHeight()){
                //Rotate the right subtree first

            }*/
           leftRotate();//Left rotation
        }
        //When a node is added, the height of the left subtree - the height of the right subtree is more than 1, and the right rotation occurs
        if (leftHeight()-rightHeight()>1){
            rightRotate();//Right choice
        }
    }

Test one:

public class AVLTreeDemo {
    public static void main(String[] args) {
        //int[] arr={4,3,6,5,7,8};
        int[] arr={10,12,8,9,7,6};
        //Create an AVLTree object
        AVLTree avlTree = new AVLTree();
        //Add node
        for (int i = 0; i < arr.length; i++) {
            avlTree.add(new Node(arr[i]));
        }
        System.out.println("Sequential traversal");
        avlTree.infixOrder();
        //System.out.println("before no balance processing");
        System.out.println("After balancing");
        System.out.println("Height of tree:"+avlTree.getRoot().height());
        System.out.println("Height of left subtree:"+avlTree.getRoot().leftHeight());
        System.out.println("Height of right subtree:"+avlTree.getRoot().rightHeight());
    }
}

The running results are as follows. It is indeed balanced.

Let's take a look at the right rotated binary tree:

6. double rotation

//When a node is added, the height of the right subtree - the height of the left subtree is more than 1, and the left rotation occurs
        if (rightHeight()-leftHeight()>1){
            //If the height of the left subtree of the right subtree of the current node is greater than the height of the right subtree of its right subtree
            if (this.right!=null&&this.right.rightHeight()<this.right.leftHeight()){
                //First rotate the right subtree to the right
                this.right.rightRotate();
                //Rotate left on current node
                this.leftRotate();
            }else {
                this.leftRotate();//Left rotation
            }
            return;//You must return
        }
        //When a node is added, the height of the left subtree - the height of the right subtree is more than 1, and the right rotation occurs
        if (leftHeight()-rightHeight()>1){
            //If the height of the right subtree of its left subtree is greater than that of its left subtree
            if (this.left!=null&&this.left.rightHeight()>this.left.leftHeight()){
                //First rotate the left subtree of the current node to the left
                this.left.leftRotate();
                //Rotate the current node to the right
                this.rightRotate();
            }else {
                //Just rotate right
                this.rightRotate();
            }
            return;
        }

7. summary

All the code is here. The most important idea is to master how to rotate. In fact, if you have made every step clear, the code is very simple to implement.

package Day42;

/**
 * @Author Zhongger
 * @Description Balanced binary tree-+-
 * @Date 2020.3.14
 */
public class AVLTreeDemo {
    public static void main(String[] args) {
        //int[] arr={4,3,6,5,7,8}; left
        //int[] arr={10,12,8,9,7,6}; right
        int[] arr={10,11,7,6,8,9};//Double rotation
        //Create an AVLTree object
        AVLTree avlTree = new AVLTree();
        //Add node
        for (int i = 0; i < arr.length; i++) {
            avlTree.add(new Node(arr[i]));
        }
        System.out.println("Sequential traversal");
        avlTree.infixOrder();
        //System.out.println("before no balance processing");
        System.out.println("After balancing");
        System.out.println("Height of tree:"+avlTree.getRoot().height());
        System.out.println("Height of left subtree:"+avlTree.getRoot().leftHeight());
        System.out.println("Height of right subtree:"+avlTree.getRoot().rightHeight());
    }
}
class AVLTree{
    private Node root;

    public Node getRoot() {
        return root;
    }

    /**
     * Returns the smallest node of the binary sort tree with node as the root node, and deletes the smallest node
     * @param node
     * @return
     */
    public int deleteRightTreeMin(Node node){
        Node target=node;
        //Loop through the left node to find the minimum
        while (target.left!=null){
            target=target.left;
        }
        //This is target, which points to the smallest node
        deleteNode(target.value);//Deleted
        return target.value;
    }

    //Find nodes to delete
    public Node search(int value){
        if (root==null){
            return null;
        }else {
            return root.search(value);
        }
    }
    //Find the parent of the node to delete
    public Node searchParent(int value){
        if (root==null){
            return null;
        }else {
            return root.searchParent(value);
        }
    }

    public void deleteNode(int value){
        if (root==null){
            return;
        }else {
            Node targetNode = search(value);
            if (targetNode==null){//No nodes found to delete
                return;
            }
            //If the targetNode does not have a parent
            if (root.left==null&&root.right==null){
                root=null;
            }
            //Parent of targetNode encountered
            Node parentNode = searchParent(value);
            //If the node to be deleted is a leaf node
            if (targetNode.left==null&&targetNode.right==null){
                //Determine whether the targetNode is the left or right child of the parent node
                if ((parentNode.left!=null)&&(parentNode.left.value==value)){
                    parentNode.left=null;
                }else if ((parentNode.right!=null)&&(parentNode.right.value==value)){
                    parentNode.right=null;
                }
            }else if (targetNode.left!=null&&targetNode.right!=null){//Delete a node with two subtrees
                int minVal = deleteRightTreeMin(targetNode.right);
                targetNode.value=minVal;
            }else {//Delete a node with only one subtree
                if (targetNode.left!=null){//Left child tree
                    if (parentNode!=null){
                        //If targetNode is the left child of parentNode
                        if (parentNode.left.value==value){
                            parentNode.left=targetNode.left;
                        }else {//targetNode is the right child of parentNode
                            parentNode.right=targetNode.left;
                        }
                    }else {
                        root=targetNode.left;
                    }

                }else {//Right subtree
                    if (parentNode!=null){
                        if (parentNode.left.value==value){
                            parentNode.left=targetNode.right;
                        }else {
                            parentNode.right=targetNode.right;
                        }
                    }else {
                        root=targetNode.right;
                    }

                }
            }
        }
    }

    //How to add nodes
    public void add(Node node){
        if (root==null){
            root=node;//If root is empty, direct root to node
        }else {
            root.add(node);
        }
    }

    //Sequential traversal
    public void infixOrder(){
        if (root!=null){
            root.infixOrder();
        }
        else {
            System.out.println("Binary sort tree is empty, unable to traverse");
        }
    }
}


class Node{//node
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    /**
     * Left rotation method
     */
    public void leftRotate(){
        //Create a new node with the value of the current root node
        Node newNode = new Node(this.value);
        //Set the new node's left subtree to the current node's left subtree
        newNode.left=this.left;
        //Set the right subtree of the new node to the left subtree of the right subtree of the current node
        newNode.right=this.right.left;
        //Replace the value of the current node with the value of the right child node
        this.value=this.right.value;
        //Set the right subtree of the current node to the right subtree of the right subtree of the current node
        this.right=this.right.right;
        //Set the left child of the current node as a new node
        this.left=newNode;
    }

    /**
     * Right rotation method
     */
    public void rightRotate(){
        //Create a new node with the value of the current root node
        Node newNode = new Node(this.value);
        //Set the right subtree of the new node as the right subtree of the current node
        newNode.right=this.right;
        //Set the left subtree of the new node as the right subtree of the left subtree of the current node
        newNode.left=this.left.right;
        //Replace the value of the current node with the value of the left child node
        this.value=this.left.value;
        //Set the left subtree of the current node to the left subtree of the left subtree of the current node
        this.left=this.left.left;
        //Set the right child of the current node as a new node
        this.right=newNode;
    }

    /**
     * Height of left subtree
     * @return
     */
    public int leftHeight(){
        if (left==null){
            return 0;
        }
        return left.height();
    }

    /**
     * Height of right subtree
     * @return
     */
    public int rightHeight(){
        if (right==null){
            return 0;
        }
        return right.height();
    }

    /**
     * Height of left and right subtrees of binary trees
     * @return
     */
    public int height(){
        return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
    }


    /**
     * Find nodes to delete
     * @param value The value of the node to delete
     * @return Return the node if found, otherwise return null
     */
    public Node search(int value){
        if (this.value==value){//This node is found
            return this;
        }else if (value<this.value){//If the value to be found is less than the current node, recursively find the left subtree
            //If the left child node is empty
            if (this.left==null){
                return null;
            }
            return this.left.search(value);
        }else {
            //If the right child node is empty
            if (this.right==null){
                return null;
            }
            return this.right.search(value);
        }
    }

    /**
     * Find the parent of the node to delete
     * @param value The value of the node to delete
     * @return Return the node if found, otherwise return null
     */
    public Node searchParent(int value){
        //If the current node is the parent of the node to be deleted, return
        if ((this.left!=null&&this.left.value==value)||
                (this.right!=null&&this.right.value==value)){
            return this;
        }else {
            //If the value to be found is less than the value of the current node, and the left node of the current node is not empty
            if (value<this.value&&this.left!=null){
                return this.left.searchParent(value);//Left subtree recursive search
            }else if (value>=this.value&&this.right!=null){
                return this.right.searchParent(value);//Recursive search of right subtree
            }else {
                return null;
            }
        }
    }

    //How to add nodes
    public void add(Node node){
        if (node==null){
            return;
        }
        //Determine the relationship between the value of the incoming node and the value of the node of the current subtree
        if (node.value<this.value){
            //If the left child of the current node is empty
            if (this.left==null){
                this.left=node;
            }else {
                //Recursively add to left subtree
                this.left.add(node);
            }
        }else {//The value of the added node is greater than the value of the current node
            if (this.right==null){
                this.right=node;
            }else {//Recursively add to right subtree
                this.right.add(node);
            }
        }
        //When a node is added, the height of the right subtree - the height of the left subtree is more than 1, and the left rotation occurs
        if (rightHeight()-leftHeight()>1){
            //If the height of the left subtree of the right subtree of the current node is greater than the height of the right subtree of its right subtree
            if (this.right!=null&&this.right.rightHeight()<this.right.leftHeight()){
                //First rotate the right subtree to the right
                this.right.rightRotate();
                //Rotate left on current node
                this.leftRotate();
            }else {
                this.leftRotate();//Left rotation
            }
            return;//You must return
        }
        //When a node is added, the height of the left subtree - the height of the right subtree is more than 1, and the right rotation occurs
        if (leftHeight()-rightHeight()>1){
            //If the height of the right subtree of its left subtree is greater than that of its left subtree
            if (this.left!=null&&this.left.rightHeight()>this.left.leftHeight()){
                //First rotate the left subtree of the current node to the left
                this.left.leftRotate();
                //Rotate the current node to the right
                this.rightRotate();
            }else {
                //Just rotate right
                this.rightRotate();
            }
            return;
        }
    }

    //Sequential traversal
    public void infixOrder(){
        if (this.left!=null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
            this.right.infixOrder();
        }
    }
}


44 original articles published, 91 praised, 20000 visitors+
Private letter follow

Tags: less Java

Posted on Sat, 14 Mar 2020 02:20:36 -0400 by fredi_bieging