数据结构之AVL树详解

yizhihongxing

数据结构之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数据结构BFS广搜法解决迷宫问题

    Java数据结构BFS广搜法解决迷宫问题 什么是BFS广搜法? 广度优先搜索(BFS)是一种遍历或搜索数据结构(例如树或图)的算法经典方法之一,也是解决迷宫问题的有效解法之一。BFS方法是从图的某个节点出发,以广度优先的方式依次访问与该节点相通的各节点,直到访问所有节点。BFS算法主要借助队列的数据结构来实现。 解决迷宫问题的具体实现 数据准备: 在解决迷宫…

    数据结构 2023年5月17日
    00
  • C语言全面梳理结构体知识点

    C语言全面梳理结构体知识点 什么是结构体? 结构体是一种自定义的数据类型,它可以包含多个不同类型的成员变量,并且这些成员变量可以通过一个变量名来访问。结构体的定义需要使用关键字struct,并且需要指定结构体的类型名和成员变量。例如: struct Person { char name[20]; int age; float height; }; 以上代码就…

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

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

    数据结构 2023年5月17日
    00
  • mosn基于延迟负载均衡算法 — 走得更快,期待走得更稳

    前言 这篇文章主要是介绍mosn在v1.5.0中新引入的基于延迟的负载均衡算法。 对分布式系统中延迟出现的原因进行剖析 介绍mosn都通过哪些方法来降低延迟 构建来与生产环境性能分布相近的测试用例来对算法进行验证 地址:https://github.com/mosn/mosn/pull/2253 在开始聊基于延迟的负载均衡算法之前,先介绍下什么是负载均衡——…

    算法与数据结构 2023年5月8日
    00
  • Java数据结构之KMP算法的实现

    Java数据结构之KMP算法的实现 1. KMP算法的概述 KMP算法的全称是Knuth-Morris-Pratt算法,是一种字符串匹配算法,用于在文本串S内查找一个模式串P的出现位置。它的特点是在P和S两个序列中,当匹配失败时,它会跳过P的部分已匹配的字符,利用这个信息来减少S和P之间的匹配次数,从而提高匹配效率。 2. KMP算法的实现 2.1 预处理失…

    数据结构 2023年5月17日
    00
  • LCA——ST表+欧拉序

    了解到一个quan新的东西: 用ST表(欧拉序)实现LCA(树上最近公共祖先) 欧拉序 前序遍历得到的序列,叫dfs序但数字可以重复出现,一进一出,叫欧拉序会发现根结点总在中间而根结点是该段序列深度最小的点因此两个点的LCA,就是在该序列上两个点第一次出现的区间内深度最小的那个点 即转化为区间RMQ问题,可以用ST表当然你可以再写一棵线段树(如果有修改操作)…

    算法与数据结构 2023年5月4日
    00
  • C++数据结构哈希表详解

    C++数据结构哈希表详解 哈希表(Hash Table)是一种非常重要的数据结构,用于实现字典等各种应用。哈希表将关键字映射到一个固定大小的数据集合中,以支持常数时间复杂度的插入、查找和删除操作。哈希表的核心思想是将关键字通过哈希函数(Hash Function)映射到数据集合中的一个索引位置,哈希函数需要满足良好的散列性质,使得关键字能够均匀地散布在数据集…

    数据结构 2023年5月17日
    00
  • JavaScript数据结构yocto queue队列链表代码分析

    JavaScript数据结构yocto queue队列链表代码分析 什么是队列? 队列(Queue)是一种基础的数据结构,属于线性结构,它的特点是在队列尾插入元素,同时在队列头删除元素,遵循先进先出(FIFO)的原则。队列可以简单的理解为排队,先到达的先被服务,而后到达的则等在队列尾排队等待。队列的应用非常广泛,例如排队系统、消息队列等。 队列的实现方式 队…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部