当前位置: 首页 > article >正文

每日学习一个数据结构-AVL树

文章目录

    • 概述
      • 一、定义与特性
      • 二、平衡因子
      • 三、基本操作
      • 四、旋转操作
      • 五、应用场景
    • Java代码实现

概述

AVL树是一种自平衡的二叉查找树,由两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明。想了解树的相关概念,请点击这里。以下是对AVL树的详细说明:

一、定义与特性

  1. 定义:AVL树是一种二叉查找树,其中每个节点的左右子树的高度差的绝对值(即平衡因子)不超过1。
  2. 特性
    • 左右子树都是AVL树。
    • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1、0、1)。
    • 任意节点的左右子树的高度差不会超过1,这保证了树的高度相对较低,从而提高了搜索、插入和删除操作的效率。

二、平衡因子

平衡因子(Balance Factor,BF)是AVL树中的一个重要概念,用于衡量节点的左右子树的高度差。平衡因子的值只能是-1、0或1。具体计算方式为:节点的右子树高度减去左子树高度。

三、基本操作

AVL树的基本操作包括插入、删除和查找,这些操作都需要在保持树平衡的前提下进行。

  1. 插入

    • 按照二叉查找树的方式插入新节点。
    • 插入后,从插入点向上回溯,更新每个节点的平衡因子。
    • 如果发现某个节点的平衡因子绝对值超过1,则进行旋转操作以恢复平衡。
  2. 删除

    • 找到要删除的节点,并将其向下旋转成一个叶子节点。
    • 直接删除该叶子节点。
    • 从删除点向上回溯,更新每个节点的平衡因子。
    • 如果发现某个节点的平衡因子绝对值超过1,则进行旋转操作以恢复平衡。
  3. 查找

    • 在AVL树中查找元素的过程与在二叉查找树中相同。
    • 由于AVL树总是保持平衡的,所以查找操作的时间复杂度为O(log n)。

四、旋转操作

旋转操作是AVL树保持平衡的关键。根据节点插入或删除后不平衡的具体情况,AVL树的旋转可以分为四种类型:

  1. 单向右旋(LL):当在节点的左子树的左子树上插入新节点导致节点不平衡(平衡因子为2)时,进行右旋转操作。
  2. 单向左旋(RR):当在节点的右子树的右子树上插入新节点导致节点不平衡(平衡因子为-2)时,进行左旋转操作。
  3. 双向旋转(先左后右,LR):当在节点的左子树的右子树上插入新节点导致节点不平衡(平衡因子为2)时,先进行左旋转再进行右旋转。
  4. 双向旋转(先右后左,RL):当在节点的右子树的左子树上插入新节点导致节点不平衡(平衡因子为-2)时,先进行右旋转再进行左旋转。

五、应用场景

AVL树适用于插入删除次数较少但查找频繁的场景。例如,Windows进程地址空间管理就采用了AVL树来实现高效的查找操作。然而,由于AVL树在插入和删除操作后需要进行复杂的旋转操作来保持平衡,所以其性能在插入和删除操作频繁的场景下可能不如其他数据结构(如红黑树)。

综上所述,AVL树是一种高效的自平衡二叉查找树,通过引入平衡因子和旋转操作来保持树的平衡性,从而提高了搜索、插入和删除操作的效率。

Java代码实现

下面是一个简单的AVL树在Java中的实现。这个实现包括了插入、删除和查找操作,以及必要的旋转操作来维持树的平衡。

class AVLTree {
    private class Node {
        int key, height;
        Node left, right;

        Node(int d) {
            key = d;
            height = 1;
        }
    }

    private Node root;

    // Utility function to get the height of the tree
    int height(Node N) {
        if (N == null)
            return 0;

        return N.height;
    }

    // Utility function to get the maximum of two integers
    int max(int a, int b) {
        return (a > b) ? a : b;
    }

    // Right rotate subtree rooted with y
    Node rightRotate(Node y) {
        Node x = y.left;
        Node T2 = x.right;

        // Perform rotation
        x.right = y;
        y.left = T2;

        // Update heights
        y.height = max(height(y.left), height(y.right)) + 1;
        x.height = max(height(x.left), height(x.right)) + 1;

        // Return new root
        return x;
    }

    // Left rotate subtree rooted with x
    Node leftRotate(Node x) {
        Node y = x.right;
        Node T2 = y.left;

        // Perform rotation
        y.left = x;
        x.right = T2;

        // Update heights
        x.height = max(height(x.left), height(x.right)) + 1;
        y.height = max(height(y.left), height(y.right)) + 1;

        // Return new root
        return y;
    }

    // Get Balance factor of node N
    int getBalance(Node N) {
        if (N == null)
            return 0;

        return height(N.left) - height(N.right);
    }

    // Insert a node with given key in the subtree rooted with node and returns the new root of the subtree
    Node insert(Node node, int key) {
        // Perform the normal BST insertion
        if (node == null)
            return (new Node(key));

        if (key < node.key)
            node.left = insert(node.left, key);
        else if (key > node.key)
            node.right = insert(node.right, key);
        else // Duplicate keys are not allowed in BST
            return node;

        // Update height of this ancestor node
        node.height = 1 + max(height(node.left), height(node.right));

        // Get the balance factor of this ancestor node to check whether this node became unbalanced
        int balance = getBalance(node);

        // If this node becomes unbalanced, then there are 4 cases

        // Left Left Case
        if (balance > 1 && key < node.left.key)
            return rightRotate(node);

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

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

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

        // Return the (unchanged) node pointer
        return node;
    }

    // Delete a node with given key in the subtree rooted with node and returns the new root of the subtree
    Node deleteNode(Node root, int key) {
        // Perform standard BST delete
        if (root == null)
            return root;

        if (key < root.key)
            root.left = deleteNode(root.left, key);
        else if (key > root.key)
            root.right = deleteNode(root.right, key);
        else {
            // node with only one child or no child
            if ((root.left == null) || (root.right == null)) {
                Node temp = null;
                if (temp == root.left)
                    temp = root.right;
                else
                    temp = root.left;

                // No child case
                if (temp == null) {
                    temp = root;
                    root = null;
                } else // One child case
                    root = temp; // Copy the contents of the non-empty child
            } else {
                // node with two children: Get the inorder successor (smallest in the right subtree)
                Node temp = minValueNode(root.right);

                // Copy the inorder successor's data to this node
                root.key = temp.key;

                // Delete the inorder successor
                root.right = deleteNode(root.right, temp.key);
            }
        }

        // If the tree had only one node then return
        if (root == null)
            return root;

        // Update height of the current node
        root.height = max(height(root.left), height(root.right)) + 1;

        // Get the balance factor of this node (to check whether this node became unbalanced)
        int balance = getBalance(root);

        // If this node becomes unbalanced, then there are 4 cases

        // Left Left Case
        if (balance > 1 && getBalance(root.left) >= 0)
            return rightRotate(root);

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

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

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

        return root;
    }

    Node minValueNode(Node node) {
        Node current = node;

        // Loop down to find the leftmost leaf
        while (current.left != null)
            current = current.left;

        return current;
    }

    // A utility function to do inorder traversal of BST
    void inorder(Node root) {
        if (root != null) {
            inorder(root.left);
            System.out.print(root.key + " ");
            inorder(root.right);
        }
    }

    // Main function
    public static void main(String[] args) {
        AVLTree tree = new AVLTree();

        /* Constructing tree given in the above figure */
        tree.root = tree.insert(tree.root, 10);
        tree.root = tree.insert(tree.root, 20);
        tree.root = tree.insert(tree.root, 30);
        tree.root = tree.insert(tree.root, 40);
        tree.root = tree.insert(tree.root, 50);
        tree.root = tree.insert(tree.root, 25);

        /* The constructed AVL Tree would be
              30
             /  \
           20   40
          /  \     \
        10   25     50
        */
        System.out.println("Inorder traversal of the constructed AVL tree is:");
        tree.inorder(tree.root);

        System.out.println("\n\nDelete 20");
        tree.root = tree.deleteNode(tree.root, 20);
        System.out.println("Inorder traversal of the modified tree is:");
        tree.inorder(tree.root);

        System.out.println("\n\nDelete 30");
        tree.root = tree.deleteNode(tree.root, 30);
        System.out.println("Inorder traversal of the modified tree is:");
        tree.inorder(tree.root);

        System.out.println("\n\nDelete 50");
        tree.root = tree.deleteNode(tree.root, 50);
        System.out.println("Inorder traversal of the modified tree is:");
        tree.inorder(tree

http://www.kler.cn/news/330995.html

相关文章:

  • Axios入门使用
  • SKD4(note上)
  • 好玩的进3D度条
  • 怎么查看是公网ip还是私网ip
  • 【web安全】——sql注入
  • 使用 Qt 和 SQLCipher 实现 SQLite 数据库加密与解密
  • 【java数据结构】顺序表
  • .Net 6.0 监听Windows网络状态切换
  • RISC-V开发 linux下GCC编译自定义指令流程笔记
  • 《开题报告》基于SSM框架的电影评论网站的设计与实现源码++学习文档+答辩讲解视频
  • H.264编解码 - I/P/B帧详解
  • C++七种异常处理
  • 【重学 MySQL】五十五、浮点和定点数据类型
  • 排序算法之——归并排序,计数排序
  • [rk3588 debain]cpu死锁问题解决
  • vue.js 原生js app端实现图片旋转、放大、缩小、拖拽
  • LeetCode讲解篇之5. 最长回文子串
  • 股票接口api,如何用excel获得股票实时数据
  • OpenAI 开发者大会2024
  • vue的el-button防止重复点击