数据结构之AVL树详解

数据结构之AVL树详解

什么是AVL树?

AVL树是一种自平衡的二叉搜索树,它的名称来自它的发明者Adelson-Velsky和Landis。在AVL树中,每个节点的左右子树的高度差(平衡因子)最多为1,否则需要通过旋转操作来重新平衡树。AVL树基于二叉搜索树,所以它包含了二叉搜索树的所有特性,同时也保证了树的高度始终处于对数级别,因此它的查找、插入、删除都具有$O(log{n})$的时间复杂度。

AVL树的旋转操作:

AVL树的插入和删除操作可能会使节点的平衡因子发生改变,因此需要通过旋转操作来维护平衡性。在AVL树中,定义左旋和右旋两种操作:

  1. 左旋:将一个节点的右子树变为新的父节点,同时原父节点成为新父节点的左子树

图例:

mermaid
graph LR;
A-->B;
B-->C;
C-->D;
D-->E;
subgraph 左旋示例
A-->|1|C;
C-->|2|B;
B-->|3|E;
E-->D;
end;

在上图中,进行了A节点的左旋操作,旋转后C节点成为了新的父节点,同时A节点变成了C节点的左子树,E节点成为了B节点的右子树。

  1. 右旋:将一个节点的左子树变为新的父节点,同时原父节点成为新父节点的右子树

图例:

mermaid
graph LR;
A-->B;
B-->C;
C-->D;
D-->E;
subgraph 右旋示例
C-->|1|A;
A-->|2|B;
B-->|3|D;
D-->E;
end;

在上图中,进行了C节点的右旋操作,旋转后A节点成为了新的父节点,同时C节点变成了A节点的右子树,D节点成为了B节点的左子树。

AVL树的插入操作:

AVL树的插入操作,首先需要按照二叉搜索树的规则将节点插入到正确的位置,然后判断是否需要进行旋转操作来保证平衡。

当插入一个节点后,其父节点的平衡因子发生了变化。如果插入节点的父节点的平衡因子为0,则将其修改为+1或-1(取决于插入节点是插入到左子树还是右子树)。如果插入节点的父节点的平衡因子为+1或-1,则将其修改为另一个值,并将祖先节点的平衡因子重新调整,并进行旋转操作。

插入操作的代码示例:

class AVLTree:
    def __init__(self):
        self.root = None

    def insert(self, key):
        self.root = self._insert(self.root, key)

    def _insert(self, node, key):
        if not node:
            return AVLNode(key)

        if key < node.key:
            node.left = self._insert(node.left, key)
        else:
            node.right = self._insert(node.right, key)

        node.update_height()

        if node.balance_factor > 1:
            if node.left.balance_factor < 0:
                node.left = self.left_rotate(node.left)
            node = self.right_rotate(node)
        elif node.balance_factor < -1:
            if node.right.balance_factor > 0:
                node.right = self.right_rotate(node.right)
            node = self.left_rotate(node)

        return node

AVL树的删除操作:

AVL树的删除操作比插入操作复杂一些,因为需要保证删除后的树仍是一棵AVL树。在删除节点时,需要分以下几种情况考虑:

  1. 如果删除的节点是叶子节点,则直接将其删除,然后调整其祖先节点的平衡因子

  2. 如果删除的节点只有一个子节点,将其子节点作为替代物,然后调整其祖先节点的平衡因子

  3. 如果删除的节点有两个子节点,则找到其右子树中最小的节点,用它来替换要删除的节点,然后删除那个节点。

删除操作的代码示例:

class AVLTree:
    def __init__(self):
        self.root = None

    def delete(self, key):
        self.root = self._delete(self.root, key)

    def _delete(self, node, key):
        if not node:
            return None

        if key < node.key:
            node.left = self._delete(node.left, key)
        elif key > node.key:
            node.right = self._delete(node.right, key)
        else:
            if not (node.left and node.right):
                node = node.left or node.right
            else:
                temp = node.right.get_min_node()
                node.key = temp.key
                node.right = self._delete(node.right, temp.key)

        if not node:
            return None

        node.update_height()

        if node.balance_factor > 1:
            if node.left.balance_factor < 0:
                node.left = self.left_rotate(node.left)
            node = self.right_rotate(node)
        elif node.balance_factor < -1:
            if node.right.balance_factor > 0:
                node.right = self.right_rotate(node.right)
            node = self.left_rotate(node)

        return node

示例说明:

示例1:

在AVL树中插入节点(1,2,3,4,5,6,7):

插入1:

graph LR;
    subgraph 插入1
        A(1);
        end;

插入2:

graph LR;
    subgraph 插入2
        A(1)-->B(2);
        end;

插入3:

graph LR;
    subgraph 插入3
        A(1)-->B(2);
        A-->C(3);
        end;

插入4:

graph LR;
    subgraph 插入4
        B(2)-->A(1);
        B-->D(4);
        A-->C(3);
        end;

插入5:

graph LR;
    subgraph 插入5
        B(2)-->A(1);
        B-->D(4);
        D-->C(3);
        D-->E(5);
        end;

插入6:

graph LR;
    subgraph 插入6
        B(2)-->D(4);
        A(1)-->C(3);
        D-->A;
        D-->F(6);
        A-->E(5);
        end;

插入7:

graph LR;
    subgraph 插入7
        D(4)-->B(2);
        D-->F(6);
        B-->A(1);
        F-->E(5);
        F-->G(7);
        end;

最终AVL树:

graph LR;
    subgraph AVL树
        D(4)-->B(2);
        D-->F(6);
        B-->A(1);
        B-->C(3);
        F-->E(5);
        F-->G(7);
        end;

示例2:

在AVL树中删除节点4:

graph LR;
    subgraph 删除4
        D(4)-->B(2);
        D-->F(6);
        B-->A(1);
        B-->C(3);
        F-->E(5);
        F-->G(7);
        D-.->A;
        end;

删除节点4后的AVL树:

graph LR;
    subgraph AVL树
        E(5)-->B(2);
        E-->D(6);
        B-->A(1);
        B-->C(3);
        D-->F(7);
        end;

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:数据结构之AVL树详解 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • Java数据结构之二叉排序树的实现

    Java数据结构之二叉排序树的实现 二叉排序树(Binary Sort Tree)是一种特殊的二叉树结构,它的每个结点都包含一个关键字,并满足以下性质: 左子树中所有结点的关键字都小于根结点的关键字; 右子树中所有结点的关键字都大于根结点的关键字; 左右子树也分别为二叉排序树。 这种结构有助于实现快速的查找、插入和删除操作。在此,我们将展示一种实现二叉排序树…

    数据结构 2023年5月17日
    00
  • Java数据结构之实现哈希表的分离链接法

    Java数据结构之实现哈希表的分离链接法 哈希表是一种非常常用的数据结构,它将数据存储在一个数组中,每个数组元素都存储着链表中的一个节点,这样可以实现高效的数据存储和查找操作。在哈希表中,我们可以通过哈希函数将关键字映射到数组中的特定位置。 但是,当哈希表的负载因子过高时,就会造成哈希冲突,这意味着两个或更多的关键字映射到了同一个数组位置。一种常见的解决方案…

    数据结构 2023年5月17日
    00
  • Python中的函数式编程:不可变的数据结构

    Python是一门支持函数式编程的语言。相比于传统的命令式编程,函数式编程更加强调数据的不可变性。本文将介绍如何在Python中使用不可变的数据结构实现函数式编程。 什么是不可变的数据结构? 不可变数据结构是指一旦创建就无法改变的数据结构。在Python中,元组(tuple)是一个典型的不可变数据结构。以下是一个创建元组的示例代码: a_tuple = (1…

    数据结构 2023年5月17日
    00
  • C语言线性表的链式表示及实现详解

    C语言线性表的链式表示及实现详解 什么是线性表 线性表是一种在计算机科学中常见的数据结构,它由一组连接在一起的元素组成,每个元素都包含前后指针以指向相邻的元素,从而构成一个连续的线性序列。线性表可以用来存储和处理一般数据集合。 链式存储结构 线性表的链式存储结构是由若干个结构体组成的链表,每个结构体都称为一个节点。每个节点包含两个字段:一个数据域用来存放数据…

    数据结构 2023年5月17日
    00
  • C++数据结构之二叉搜索树的实现详解

    C++数据结构之二叉搜索树的实现详解 1. 什么是二叉搜索树? 二叉搜索树是一种二叉树,其中每个节点都包含一个键值,且每个节点的键值都大于其左子树中任何节点的键值,小于其右子树中任何节点的键值。如下图所示: 9 / \ 4 15 / \ 12 20 在上面的二叉搜索树中,节点的键值分别是9, 4, 15, 12, 20,且每个节点的键值都符合上述定义。 2.…

    数据结构 2023年5月17日
    00
  • Java数据结构之对象比较详解

    Java数据结构之对象比较详解 在Java中,比较两个对象的内容是否相等一直是程序员们比较困惑的问题。本文将详细探讨Java中对象比较的几种方式,并给出相应的示例。 基本类型比较 在Java中,比较基本类型的值可以使用双等号(==)进行判断。例如: int a = 1; int b = 1; boolean result = a == b; System.o…

    数据结构 2023年5月17日
    00
  • 一文了解mysql索引的数据结构为什么要用B+树

    MySQL索引的数据结构主要为B+树,那么B+树为什么是最适合作为索引数据结构呢?在介绍B+树之前,我们先来了解下B树。 B树B树是一棵多路平衡查找树,也称为B-树(B-tree),主要应用在文件系统和数据库中,可以显著减少I/O操作次数。B树的每个节点存储的元素个数比二叉查找树的节点多,且节点存储的元素是按顺序排列的,这些特点使得在查找过程中可以快速定位需…

    数据结构 2023年5月17日
    00
  • 「分治」黑白棋子的移动

    本题为3月23日23上半学期集训每日一题中A题的题解 题面 题目描述 有2n个棋子(n≥4)排成一行,开始位置为白子全部在左边,黑子全部在右边,如下图为n=5的情形: ○○○○○●●●●● 移动棋子的规则是:每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能…

    算法与数据结构 2023年4月18日
    00
合作推广
合作推广
分享本页
返回顶部