Introduction

The balanced binary search tree is a special kind of binary search tree. Why is there a balanced binary search tree?

Consider the special case of a binary search tree. If all nodes of a binary search tree are right nodes, then the binary search tree will degenerate into a linked list. As a result, the time complexity of the search becomes O(n), where n is the number of nodes in the binary search tree.

The balanced binary search tree is created to solve this problem. It reduces the time complexity to O(logn) by limiting the height of the tree.

Characteristics of AVL

Before discussing the characteristics of AVL, we first introduce a concept called balance factor, which represents the height difference between the left subtree and the right subtree.

If the balance factor = 0, it means that this is a fully balanced binary tree.

If the balance factor = 1, then this tree is a balanced binary tree AVL.

That is to say, the balance factor of AVL cannot be greater than 1.

Let's look at an AVL example:

To sum up, AVL is first a binary search tree, and then a binary balanced tree.

Construction of AVL

With the characteristics of AVL, let's take a look at how AVL is constructed.

public class AVLTree {

    //根节点
    Node root;

    class Node {
        int data; //节点的数据
        int height; //节点的高度
        Node left;
        Node right;

        public Node(int data) {
            this.data = data;
            left = right = null;
        }
    }

Similarly, AVL is also composed of various nodes, and each node has several attributes: data, left, and right.

Because it is a binary balanced tree, whether the node is balanced is also related to the height of the node, so we also need to define a height as the height of the node.

Here are two auxiliary methods, one is to get a given node height:

//获取给定节点的高度
    int height(Node node) {
        if (node == null)
            return 0;
        return node.height;
    }

And get the balance factor:

//获取平衡因子
    int getBalance(Node node) {
        if (node == null)
            return 0;
        return height(node.left) - height(node.right);
    }

AVL search

The search method of AVL is the same as that of binary search tree.

First look at an intuitive example, how to search for the node 7 in AVL:

The basic steps for searching are:

  1. Starting from the root node 15, compare the size of the root node and the search value
  2. If the search value is less than the node value, then recursively search the left tree
  3. If the search value is greater than the node value, then the tree on the right is recursively searched
  4. If the node matches, just return directly.

The corresponding java code is as follows:

//搜索方法,默认从根节点搜索
    public Node search(int data){
        return search(root,data);
    }

    //递归搜索节点
    private Node search(Node node, int data)
    {
        // 如果节点匹配,则返回节点
        if (node==null || node.data==data)
            return node;

        // 节点数据大于要搜索的数据,则继续搜索左边节点
        if (node.data > data)
            return search(node.left, data);

        // 如果节点数据小于要搜素的数据,则继续搜索右边节点
        return search(node.right, data);
    }

Insertion of AVL

The insertion of AVL is the same as the insertion of BST, but after the insertion, the tree may no longer be balanced, so we need to do a rebalancing step.

Watch an intuitive animation:

The logic of insertion is like this:

  1. Starting from the root node, compare the node data and the data to be inserted
  2. If the data to be inserted is less than the node data, recursively insert the left subtree
  3. If the data to be inserted is greater than the node data, recursively insert the right subtree
  4. If the root node is empty, insert the current data as the root node

After inserting the data, we need to rebalance.

The logic of rebalancing is this:

  1. Find the first unbalanced node upwards from the inserted node, this node is denoted as z
  2. Rotate the subtree with z as the root node to obtain a balanced tree.

According to the different tree with z as the root node, we have four rotation methods:

  • left-left:

If it is a left-left tree, then a right-hand rotation is enough.

What about the right-handed steps?

  1. Find the left node y of the z node
  2. Use y as the root node after rotation
  3. z as the right node of y
  4. The right node of y is the left node of z
  5. Update the height of z

The corresponding code is as follows:

Node rightRotate(Node node) {
        Node x = node.left;
        Node y = x.right;

        // 右旋
        x.right = node;
        node.left = y;

        // 更新node和x的高度
        node.height = max(height(node.left), height(node.right)) + 1;
        x.height = max(height(x.left), height(x.right)) + 1;

        // 返回新的x节点
        return x;
    }
  • right-right:

If it is a right-right form of tree, it needs to go left-handed once:

The left-handed steps are just the opposite of the right-handed steps:

  1. Find the right node y of the z node
  2. Use y as the root node after rotation
  3. z as the left node of y
  4. The left node of y is the right node of z
  5. Update the height of z

The corresponding code is as follows:

//左旋
    Node leftRotate(Node node) {
        Node x = node.right;
        Node y = x.left;

        //左旋操作
        x.left = node;
        node.right = y;

        // 更新node和x的高度
        node.height = max(height(node.left), height(node.right)) + 1;
        x.height = max(height(x.left), height(x.right)) + 1;

        // 返回新的x节点
        return x;
    }
  • left-right:

In the case of left and right, you need to perform a left rotation to convert the tree into a left left format, and then perform a right rotation to get the final result.

  • right-left:

If it is the right left format, you need to perform a right rotation first, convert it to the right right format, and then perform a left rotation.

Now the question is, how to judge which format a tree is? We can judge by comparing the obtained balance factor with the newly inserted data:

  1. If balance>1, then we are in Left Left or Left Right situation, at this time we need to compare the size of newly inserted data and node.left.data

    If data <node.left.data, it means that it is left and left, and it only needs to rotate right once

    If data> node.left.data, it means that it is left and right. You need to rotate node.left once to the left, and then rotate node to the right once

  2. If balance<-1, then we are in the situation of Right Right or Right Left. At this time, we need to compare the size of the newly inserted data and node.right.data
    If data> node.right.data, it means that it is Right Right, and it only needs to rotate left once.

    If data <node.left.data, it means that it is a right left situation, you need to rotate node.right once to the right, and then rotate node to the left once

The final code for inserting the node is as follows:

//插入新节点,从root开始
    public void insert(int data){
        root=insert(root, data);
    }

    //遍历插入新节点
    Node insert(Node node, int data) {

        //先按照普通的BST方法插入节点
        if (node == null)
            return (new Node(data));

        if (data < node.data)
            node.left = insert(node.left, data);
        else if (data > node.data)
            node.right = insert(node.right, data);
        else
            return node;

        //更新节点的高度
        node.height = max(height(node.left), height(node.right)) + 1;

        //判断节点是否平衡
        int balance = getBalance(node);

        //节点不平衡有四种情况
        //1.如果balance>1,那么我们在Left Left或者left Right的情况,这时候我们需要比较新插入的data和node.left.data的大小
        //如果data < node.left.data,表示是left left的情况,只需要一次右旋即可
        //如果data > node.left.data,表示是left right的情况,则需要将node.left进行一次左旋,然后将node进行一次右旋
        //2.如果balance<-1,那么我们在Right Right或者Right Left的情况,这时候我们需要比较新插入的data和node.right.data的大小
        //如果data > node.right.data,表示是Right Right的情况,只需要一次左旋即可
        //如果data < node.left.data,表示是Right left的情况,则需要将node.right进行一次右旋,然后将node进行一次左旋

        //left left
        if (balance > 1 && data < node.left.data)
            return rightRotate(node);

        // Right Right
        if (balance < -1 && data > node.right.data)
            return leftRotate(node);

        // Left Right
        if (balance > 1 && data > node.left.data) {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }

        // Right Left
        if (balance < -1 && data < node.right.data) {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

        //返回插入后的节点
        return node;
    }

Deletion of AVL

AVL deletion is similar to insertion.

First delete according to the ordinary BST, and then also need to do rebalancing.

Watch an intuitive animation:

After deletion, there are 4 situations in which nodes are rebalanced:

  1. If balance>1, then we are in Left Left or Left Right situation, at this time we need to compare the balance factor of the left node

    If the balance factor of the left node is >=0, it means that it is left and left, and it only needs to rotate right once

    If the balance factor of the left node is <0, it means that it is left and right. You need to rotate node.left to the left and then rotate the node to the right.

  2. If balance<-1, then we are in the situation of Right Right or Right Left. At this time, we need to compare the balance factor of the right node

    If the balance factor of the right node is <=0, it means that it is Right Right, and it only needs to rotate left once.

    If the balance factor of the right node>0, it means that it is a right left situation, and you need to rotate node.right to the right, and then rotate the node to the left.

The corresponding code is as follows:

Node delete(Node node, int data)
    {
        //Step 1. 普通BST节点删除
        // 如果节点为空,直接返回
        if (node == null)
            return node;

        // 如果值小于当前节点,那么继续左节点删除
        if (data < node.data)
            node.left = delete(node.left, data);

        //如果值大于当前节点,那么继续右节点删除
        else if (data > node.data)
            node.right = delete(node.right, data);

       //如果值相同,那么就是要删除的节点
        else
        {
            // 如果是单边节点的情况
            if ((node.left == null) || (node.right == null))
            {
                Node temp = null;
                if (temp == node.left)
                    temp = node.right;
                else
                    temp = node.left;

                //没有子节点的情况
                if (temp == null)
                {
                    node = null;
                }
                else // 单边节点的情况
                    node = temp;
            }
            else
            {  //非单边节点的情况
                //拿到右侧节点的最小值
                Node temp = minValueNode(node.right);
                //将最小值作为当前的节点值
                node.data = temp.data;
                // 将该值从右侧节点删除
                node.right = delete(node.right, temp.data);
            }
        }

        // 如果节点为空,直接返回
        if (node == null)
            return node;

        // step 2: 更新当前节点的高度
        node.height = max(height(node.left), height(node.right)) + 1;

        // step 3: 获取当前节点的平衡因子
        int balance = getBalance(node);

        // 如果节点不再平衡,那么有4种情况
        //1.如果balance>1,那么我们在Left Left或者left Right的情况,这时候我们需要比较左节点的平衡因子
        //如果左节点的平衡因子>=0,表示是left left的情况,只需要一次右旋即可
        //如果左节点的平衡因<0,表示是left right的情况,则需要将node.left进行一次左旋,然后将node进行一次右旋
        //2.如果balance<-1,那么我们在Right Right或者Right Left的情况,这时候我们需要比较右节点的平衡因子
        //如果右节点的平衡因子<=0,表示是Right Right的情况,只需要一次左旋即可
        //如果右节点的平衡因子>0,表示是Right left的情况,则需要将node.right进行一次右旋,然后将node进行一次左旋
        // Left Left Case
        if (balance > 1 && getBalance(node.left) >= 0)
            return rightRotate(node);

        // Left Right Case
        if (balance > 1 && getBalance(node.left) < 0)
        {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }

        // Right Right Case
        if (balance < -1 && getBalance(node.right) <= 0)
            return leftRotate(node);

        // Right Left Case
        if (balance < -1 && getBalance(node.right) > 0)
        {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }
        return node;
    }

The code address of this article:

learn-algorithm

This article is included in http://www.flydean.com/11-algorithm-avl-tree/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "Program those things", know technology, know you better!


flydean
890 声望433 粉丝

欢迎访问我的个人网站:www.flydean.com