AVL 树操作2025年3月17日 | 阅读 15 分钟 1962 年,GM Adelson-Velsky 和 EM Landis 创建了 AVL 树。为了纪念创造者,这棵树被称为 AVL。 AVL 树的定义是高度平衡的二叉搜索树,其中每个节点都有一个平衡因子,该平衡因子是通过从左子树的高度中减去右子树的高度来确定的。 如果每个节点的平衡因子介于 -1 和 1 之间,则认为树是平衡的;否则,需要平衡树。 平衡因子平衡因子 (k) = 高度 (左(k)) - 高度 (右(k))
为什么要使用 AVL 树?大多数 BST 操作,包括搜索、最大值、最小值、插入、删除等,都需要 O(h) 时间,其中 h 是 BST 的高度。对于倾斜的二叉树,这些操作的成本可能会增加到 O(n)。如果我们确保在每次插入和删除后树的高度保持 O(log(n)),我们可以为所有这些操作提供 O(log(n)) 的上限。AVL 树的高度始终为 O(log(n)),其中 n 是树的节点数。 AVL 树上的操作由于 AVL 树也是二叉搜索树,因此所有操作的执行方式与二叉搜索树中的操作相同。搜索和遍历不会导致 AVL 树属性的违反。然而,可能破坏此条件的动作是插入和删除;因此,需要对其进行审查。
AVL 树的插入为了确保在每次插入后提供的树保持 AVL,我们必须在典型的 BST 插入过程中添加一些重新平衡。 以下两个简单的操作(keys(left) key(root) keys(right)) 可以在不违反 BST 属性的情况下用于平衡 BST。
插入的步骤如下
下面列出了在上述四种情况下要执行的过程。在每种情况下,我们只需要重新平衡以 z 为根的子树,并且一旦以 z 为根的子树的高度等于其插入前的值(通过适当的旋转),整个树就会平衡。 1. 左左情况 ![]() 2. 左右情况 ![]() 3. 右右情况 ![]() 4. 右左情况 ![]() 插入方法概念是利用递归 BST 插入,在插入后,我们从底层获得指向每个祖先节点的指针。因此,不需要父节点指针就可以向上移动。递归代码本身会向上遍历并访问之前插入的每个节点。 为了将这个概念付诸实践,请遵循以下步骤:
插入程序输出 Preorder traversal of the constructed AVL tree is 30 20 10 25 40 50 ...................................................................................... Process executed in 1.22 seconds Press any key to continue 说明 在旋转操作(左旋和右旋)期间,只更新了几个点,因此所需时间是常数。更新高度和获取平衡因子也需要恒定的时间。因此,AVL 插入的时间复杂度与 BST 插入相同,即 O(h),其中 h 是树的高度。由于 AVL 树是平衡的 (Logn),因此高度为 O。因此,AVL 插入的时间复杂度为 O(Logn)。 与红黑树的比较红黑树 红黑树是一种自平衡二叉搜索树,其中每个节点都有一个额外的位,通常理解为颜色(红色或黑色)。通过使用这些颜色,可以在插入和删除期间保持树的平衡。尽管树的平衡不是完美的,但它足以减少搜索时间并将其保持在 O(log n) 或以下,其中 n 是树组件的总数。Rudolf Bayer 于 1972 年发明了这种树。 通过使用 AVL 树和其他自平衡搜索树(如红黑树),所有基本操作都可以在 O(log n) 时间内完成。与红黑树相比,AVL 树的分布更均匀,尽管在插入和删除期间可能会导致更多的旋转。因此,如果您的应用程序包含频繁的插入和删除操作,则建议使用红黑树。此外,如果搜索的频率更高,而插入和删除的频率较低,则应选择 AVL 树而不是红黑树。 AVL 树中的删除在标准的 BST 删除过程中,我们必须添加一些重新平衡操作,以确保每次删除后,提供的树仍然是 AVL 树。以下两个简单的操作(keys(left) key(root) keys(right)) 可以在不违反 BST 属性的情况下用于重新平衡 BST。
树的 T1、T2 和 T3 子树由 y(在左侧)或 x(在右侧)根植。 ![]() 上述两棵树中键的顺序如下: keys(T1) < key(x) < keys(T2) < key(y) < keys(T3) 令 w 为被删除的节点
与插入一样,上述 4 种情况下的执行步骤也相同。请注意,与插入不同,修复节点 z 可能无法完全修复 AVL 树。 1. 左左情况 ![]() 2. 左右情况 ![]() 3. 右右情况 ![]() 4. 右左情况 ![]() 与插入不同,在删除操作中,在 z 处的旋转之后,可能需要在 z 的祖先处进行进一步的旋转。为了到达根节点,我们必须继续沿着路径前进。 删除方法下面提供了 AVL 树的 C 语言删除实现。递归 BST 删除是以下 C 语言实现的基础。在递归 BST 删除期间,我们从底层逐个获得指向每个祖先节点的指针。因此,向上移动不需要父节点指针。递归代码本身会向上遍历并访问被删除节点的每个祖先。
C 语言删除程序输出 Preorder traversal of the constructed AVL tree is 9 1 0 -1 5 2 6 10 11 Preorder traversal after deletion of 10 1 0 -1 9 5 2 6 11 ..................................................................................... Process executed in 2.11 seconds Press any key to continue 说明 在旋转操作(左旋和右旋)期间,只更新了少量点,因此所需时间是恒定的。获取平衡因子和更新高度都需要时间。因此,AVL 删除的时间复杂度为 O(h),其中 h 是树的高度,与 BST 删除相同。由于 AVL 树的平衡性 (Logn),因此高度为 O。因此,AVL 删除的时间复杂度为 O。(Log n)。 AVL 树的优点
总结
下一主题数据结构中栈的局限性 |
我们请求您订阅我们的新闻通讯以获取最新更新。