>

>
1x
go to beginning previous frame pause play next frame go to end

二叉搜索树(BST)是一种特殊的二叉树,每个顶点最多可以有两个子节点。这种结构遵循BST属性,规定给定顶点的左子树中的每个顶点的值必须小于给定顶点的值,右子树中的每个顶点的值必须大于给定顶点的值。这个可视化实现了 'multiset' 属性:虽然所有的键都是不同的整数,但重复整数的信息被存储为频率属性(只显示出现多次的键)。作为演示,使用 Search(7) 函数来动画显示在上面随机生成的BST中搜索范围在1到99的随机值。


Adelson-Velskii Landis(AVL)树是一种自平衡的BST,它保持其高度在对数阶(O(log N)相对于AVL树中存在的顶点数(N)。


Remarks: By default, we show e-Lecture Mode for first time (or non logged-in) visitor.
If you are an NUS student and a repeat visitor, please login.

🕑

要在标准二叉搜索树和AVL树(主要在插入和删除整数时有所不同)之间切换,请选择相应的标题。


我们还提供一个URL快捷方式,可以快速访问AVL树模式,可在https://visualgo.net/en/avl找到。URL中的 'en' 可以替换为您首选语言的两个字符代码(如果有的话)。


Pro-tip 1: Since you are not logged-in, you may be a first time visitor (or not an NUS student) who are not aware of the following keyboard shortcuts to navigate this e-Lecture mode: [PageDown]/[PageUp] to go to the next/previous slide, respectively, (and if the drop-down box is highlighted, you can also use [→ or ↓/← or ↑] to do the same),and [Esc] to toggle between this e-Lecture mode and exploration mode.

🕑

BST,特别是像AVL树这样的平衡BST,是实现某种类型(或映射)抽象数据类型(ADT)的有效数据结构。


表ADT应该有效地支持至少以下三种操作:

  1. Search(v) — 确定v是否存在于ADT中,
  2. Insert(v) — 将v添加到ADT中,
  3. Remove(v) — 从ADT中删除v

对于类似的讨论,请参考哈希表电子讲座幻灯片


Pro-tip 2: We designed this visualization and this e-Lecture mode to look good on 1366x768 resolution or larger (typical modern laptop resolution in 2021). We recommend using Google Chrome to access VisuAlgo. Go to full screen mode (F11) to enjoy this setup. However, you can use zoom-in (Ctrl +) or zoom-out (Ctrl -) to calibrate this.

🕑

我们正在讨论一种特殊类型的表ADT,其中的键必须是有序的。这与允许无序键的其他类型的表ADT形成对比。


这种表ADT类型的具体要求将在后续幻灯片中进行阐明。


Pro-tip 3: Other than using the typical media UI at the bottom of the page, you can also control the animation playback using keyboard shortcuts (in Exploration Mode): Spacebar to play/pause/replay the animation, / to step the animation backwards/forwards, respectively, and -/+ to decrease/increase the animation speed, respectively.

🕑

使用未排序的数组或向量来实现表ADT可能会导致效率低下:

  1. Search(v)的时间复杂度为O(N),因为如果v不存在,我们可能需要遍历ADT的所有N个元素,
  2. Insert(v)可以用O(1)的时间复杂度实现,只需在数组的末尾追加v
  3. Remove(v)的运行时间复杂度也为O(N),因为我们首先需要搜索v(已经是O(N)),然后关闭由删除操作产生的间隙 —— 这也是一个O(N)的操作。
🕑

排序的数组或向量实现表ADT可以提高Search(v)的性能,但这是以牺牲Insert(v)性能为代价的:

  1. 现在可以用时间复杂度为O(log N)来实现Search(v),因为我们可以在排序的数组上使用二分查找策略,
  2. Insert(v)现在的操作时间复杂度为O(N),因为我们需要使用类似插入排序的策略来确保数组保持排序,
  3. Remove(v)仍然在O(N)时间复杂度下运行。虽然Search(v)在O(log N)下操作,但我们仍然需要关闭由删除引起的间隙,这在O(N)下运行。
🕑

本电子讲座的目标是介绍BST和平衡BST数据结构,即AVL树,它们使我们能够实现基本的表ADT操作,如 Search(v),Insert(v) 和 Remove(v) —— 以及其他一些表ADT操作(参见下一张幻灯片)——在O(log N) 时间内。这个时间复杂度明显小于 N。请尝试下面的交互式滑块,感受这个显著的差异。



log N = 20, N = 1048576.


PS: 更有经验的读者可能会注意到存在另一种数据结构,它可以更快地执行这三个基本的表ADT操作。但是,请继续阅读...

🕑

除了基本的三种操作外,还有几种其他的表ADT操作:

  1. 找到 Min()/Max() 元素,
  2. 找到 Successor(v),或者说是'下一个更大'的元素,和 Predecessor(v),或者说是'前一个更小'的元素,
  3. 按排序顺序列出元素,
  4. Rank(v) & Select(k)
  5. 还有其他可能的操作。

讨论:给定使用排序或未排序的数组/向量的约束,对于上述的前三个附加操作,最优的实现方式是什么?

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

可用于实现表ADT的更简单的数据结构是链接列表

Quiz: Can we perform all basic three Table ADT operations: Search(v)/Insert(v)/Remove(v) efficiently (read: faster than O(N)) using Linked List?

No
Yes

讨论:为什么?

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

另一个可以用来实现表ADT的数据结构是哈希表。它具有非常快的 Search(v)、Insert(v) 和 Remove(v) 性能(所有这些都在预期的 O(1) 时间内)。


Quiz: So what is the point of learning this BST module if Hash Table can do the crucial Table ADT operations in unlikely-to-be-beaten expected O(1) time?

There are valid reasons, which are ____
There is no point, so this BST module can be ignored


讨论上述答案!提示:回到前4张幻灯片

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

我们现在将介绍BST数据结构。请参考上面提供的一个示例BST的可视化!


在BST中,根顶点是唯一的,没有父节点。相反,叶顶点,可以有几个,没有子节点。不是叶子的顶点被称为内部顶点。有时,根顶点不包括在内部顶点的定义中,因为只有一个顶点(即根顶点)的BST在技术上也可以符合叶子的定义。


在插图的例子中,顶点15是根,顶点5、7和50是叶子,顶点4、6、15(也是根)、23和71是内部顶点。

🕑

每个顶点都有几个关键属性:指向左子节点的指针,指向右子节点的指针,指向父顶点的指针,键/值/数据,以及为实现 'multiset' 的这个可视化特别添加的:每个键的频率(可能还有其他属性)。并非所有属性都会用于所有顶点,例如,叶顶点的左右子属性都会 = NULL。其他一些实现将键(用于在BST中排序顶点)与与键关联的实际卫星数据分开。


顶点的左/右子(除叶子外)分别在该顶点的左/右和下方绘制。顶点的父(除根外)在该顶点上方绘制。每个顶点的(整数)键在代表该顶点的圆内绘制,如果有相同(整数)键的重复插入,将会有一个额外的连字符 '-' 和该键的实际频率(≥ 2)。在上面的例子中,(键)15的左子是6,右子是23。因此,6(和23)的父是15。一些键可能会以随机方式有 '-'(实际频率)。


讨论:实际上可以省略每个顶点的父指针。如何做到?

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

在这个可视化中,我们允许重复的整数,通过保持N(整数)键的不同,但任何现有键的重复将被存储为该键的'frequency'属性(可视化为'-'(实际频率,但只有当它≥2时))。因此,我们可以使用简单的BST属性,如下:对于每个顶点X,X的左子树上的所有顶点都严格小于X,X的右子树上的所有顶点都严格大于X。


在上面的例子中,根15的左子树上的顶点:{4, 5, 6, 7}都小于15,根15的右子树上的顶点:{23, 50, 71}都大于15。你也可以递归地检查其他顶点的BST属性。


在这个可视化中,我们允许键在[-99..99]的范围内。

🕑

我们为以下常见的 BST/AVL 树操作提供可视化:

  1. 查询操作(BST 结构保持不变):
    1. Search(v)(或 LowerBound(v)),
    2. Predecessor(v)(以及类似的 Successor(v)),以及
    3. Inorder/Preorder/Postorder 遍历,
  2. 更新操作(BST 结构(很可能)会改变):
    1. 创建 BST(几个标准),
    2. Insert(v),以及
    3. Remove(v)。
🕑

在 VisuAlgo 中,还有一些其他的 BST (查询) 操作还未被可视化:

  1. Rank(v):给定一个键v,确定它在 BST 元素排序顺序中的排名(基于1的索引)。也就是说,Rank(FindMin()) = 1 和 Rank(FindMax()) = N。如果v不存在,我们可以报告 -1。
  2. Select(k):给定一个排名k,1 ≤ kN,确定在 BST 中具有该排名k的键v。或者换句话说,找到 BST 中第k小的元素。也就是说,Select(1) = FindMin() 和 Select(N) = FindMax()。

这两个操作的详细信息目前在某个 NUS 课程中被隐藏,以便于教学。

🕑

如果没有(或很少)更新,特别是插入和/或删除操作,那么这种数据结构就被称为静态 (static) 数据结构。


即使有很多更新操作,也能保持高效的数据结构被称为动态 (dynamic) 数据结构。二叉搜索树(BST)和特别是平衡二叉搜索树(例如,AVL树)属于这一类别。

🕑

由于数据(对于这个可视化来说是不同的整数)在BST中的组织方式,我们可以二分搜索一个整数v(这就是二叉搜索树的名字的由来)。


首先,我们将当前顶点设置为根,然后检查当前顶点是小于/等于/大于我们正在搜索的整数v。然后我们分别进入右子树/停止/进入左子树。我们一直这样做,直到我们找到所需的顶点或者我们找不到。


在上面的BST示例中,尝试点击 Search(23)(在2次比较后找到),Search(7)(在3次比较后找到),Search(21)(在2次比较后未找到 - 在这一点上我们会意识到我们找不到21)。

🕑

请注意,这个术语是基于C++ std::set::lower_bound中给出的定义。其他编程语言,例如,Java TreeSet有一个类似的方法 "higher()"。


如果v存在于BST中,那么 lower_bound(v)与Search(v)相同。但是,如果v不存在于BST中,lower_bound(v)将找到BST中严格大于v的最小值(除非v > BST中的最大元素)。如果稍后将这个当前不存在的v插入到这个BST中,这就是它的位置。

🕑

同样,由于BST内部数据的组织方式,我们可以通过从根开始,分别向左/右子树不断前进,找到最小/最大元素(在这个可视化中是一个整数)。


尝试点击上面示例BST中的 SearchMin()SearchMax()。答案应该是4和71(分别在与从根到最左顶点/最右顶点的3个整数比较后得出)。

🕑

Search(v)/lower_bound(v)/SearchMin()/SearchMax() 操作在 O(h) 中运行,其中 h 是 BST 的高度。


但请注意,如上面的随机 '偏右' 示例所示,这个 h 在普通 BST 中可以高达 O(N)。尝试 Search(100)(这个值不应该存在,因为我们只使用 [1..99] 之间的随机整数来生成这个随机 BST,因此 Search 程序应该从根检查到唯一的叶子,时间为 O(N) —— 效率不高。

🕑
由于BST属性,我们可以找到整数 v 的后继(假设我们通过之前调用 Search(v) 已经知道整数 v 的位置),如下所示:
  1. 如果 v 具有右子树,则 v 的右子树中的最小整数必须是 v 的后继。尝试Successor(23)(应为50)。
  2. 如果 v 没有右子树,我们需要遍历 v 的祖先,直到我们找到'右转'到顶点 w(或者,直到我们发现第一个大于顶点 v 的顶点 w)。 一旦我们找到顶点 w,我们将看到顶点 v w 的左子树中的最大元素。 试试Successor(7)(应该是15)。
  3. 如果 v 是BST中的最大整数,则 v 没有后继。 试试Successor(71)(应该没有)。
🕑
整数v的前驱的操作类似地定义(只是后继操作的镜像)。
尝试相同的三个镜像的极端情况:Predecessor(6)(应为5),Predecessor(50)(应为23),Predecessor(4)(应为无/none)。
此时,请暂停一会并思考这三个后继(v)/ 前驱(v)案例,以确保您理解这些概念。
🕑
前驱(v)和后继(v)操作在 O(h) 中运行,其中h是BST的高度。
但请记住,这个h可以和正常BST中的 O(N) 一样高,如上面的随机“倾斜右侧”示例所示。 如果我们调用Successor(FindMax()),我们将在 O(N) 时间从最后一个叶子回到根 - 效率不高。
🕑
我们可以执行这个BST的Inorder Traversal(中序遍历)来获得这个BST中的排序整数列表(事实上,如果我们将BST压平成一行,我们将看到顶点是从最小/最左边到最大/最右边来排序的)。

中序遍历是一种递归方法,我们首先访问左子树,遍历左子树中的所有项,访问当前子树的根,然后浏览右子树和右子树中的所有项。 不用多说了,让我们尝试Inorder Traversal,看看它在上面的BST示例中如何操作。
🕑

无论BST的高度如何,Inorder Traversal都以O(N)运行。
讨论:为什么?
PS:有些人调用N个无序整数插入O(N log N)中的BST,然后执行O(N)Inorder Traversal作为'BST sort'。 它很少使用,因为有几种比这更容易使用(基于比较)的排序算法

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

我们已经包含了前序遍历和后序遍历树的动画方法。


基本上,在前序遍历中,我们在访问左子树和右子树之前先访问当前的根。对于背景中显示的示例BST,我们有:{{15},{6, 4, 5, 7},{23, 71, 50}}。

PS:你注意到递归模式了吗?根,根的左子树的成员,根的右子树的成员。


在后序遍历中,我们先访问左子树和右子树,然后再访问当前的根。对于背景中显示的示例BST,我们有:{{5, 4, 7, 6},{50, 71, 23},{15}}。


讨论:给定一个BST的前序遍历,例如[15, 6, 4, 5, 7, 23, 71, 50],你能用它恢复原始的BST吗?对于后序遍历的类似问题也是可能的。

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

我们可以通过执行类似于Search(v)的操作将新的整数插入到BST中。但是这次,我们不再报告新的整数未找到,而是在插入点创建一个新的顶点,并将新的整数放在那里。尝试在上面的例子中使用 Insert(60)(第一次插入将创建一个新的顶点,但请看下面)。


由于我们现在实现了 'multiset',我们可以插入重复的元素,例如,尝试在上面的例子中使用 Insert(7)(多次)或再次点击 Insert(60)(重复的)。

🕑
插入(v)在O(h)中运行,其中h是BST的高度。
到目前为止你应该知道这个 h 在正常BST中可以和 O(N) 一样高,如上面随机的“向右倾斜”示例所示。 如果我们调用Insert(FindMax()+1),即我们插入一个大于当前最大值的新整数,我们将在O(N)时间内从根向下移动到最后一个叶子,然后插入新整数作为最后一个叶子的右子节点 - 效率不高(请注意,在此可视化中我们最多只允许h=9)。
🕑

Quiz: Inserting integers [1,10,2,9,3,8,4,7,5,6] one by one in that order into an initially empty BST will result in a BST of height:

8
10
The height cannot be determined
9

专业提示:您可以使用“探索模式”来验证答案。
🕑

我们可以通过执行类似于 Search(v) 的操作来从BST中删除一个整数。


如果在BST中找不到 v,我们就什么都不做。


如果在BST中找到了 v,我们不会报告找到了现有的整数 v,而是进行以下检查。如果 v 的频率 ≥ 2,我们只需将其频率减一,而不做任何其他操作。然而,如果 v 的频率正好为1,我们将执行三种可能的删除情况之一,这将在三个单独的幻灯片中详细说明(我们建议你逐一尝试它们)。

🕑

第一个情况是最简单的:顶点 v 目前是BST的叶子顶点之一。


删除叶子顶点非常简单:我们只需删除那个叶子顶点 - 尝试在上面的BST示例上点击 Remove(5)(如果随机化导致顶点5有多于一个的副本,只需再次点击该按钮)。


这部分显然是 O(1) ——在早先的 O(h) 搜索类似的努力之上。

🕑

第二种情况也不是那么难:顶点 v 是 BST 的(内部/根)顶点,并且它有 恰好一个子节点。如果不做任何其他操作就删除 v,将会断开 BST。


删除只有一个子节点的顶点并不难:我们将该顶点的唯一子节点与该顶点的父节点连接起来 - 尝试在上面的 BST 示例上点击 Remove(23)(如果随机化导致顶点 23 有多于一个的副本,只需再次点击该按钮)。


这部分也显然是 O(1) ——在早先的 O(h) 搜索类似的努力之上。

🕑

三个情况中,第三个情况是最复杂的:顶点 v 是BST的(内部/根)顶点,它有正好两个子节点。如果不做任何其他操作就删除 v,将会断开BST。


删除具有两个子节点的顶点的方法如下:我们用它的后继顶点替换该顶点,然后在其右子树中删除其重复的后继顶点 - 尝试在上面的示例BST上 Remove(6)(如果随机化导致顶点6有多于一个的副本,只需再次点击该按钮)。


由于需要找到后继顶点,这部分需要 O(h) —— 除了之前的 O(h) 搜索类似的努力。

🕑

本案例3值得进一步讨论:

  1. 为什么用后继C替换有两个子节点的顶点B总是一个有效的策略?
  2. 我们可以用它的前身A替换有两个子节点的顶点B吗? 为什么或者为什么不?
🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

Remove(v) 的运行时间为 O(h),其中 h 是 BST 的高度。删除情况 3(删除具有两个子节点的顶点是最“重”的,但它不超过 O(h))。


如您现在应该完全理解的,h 在正常的 BST 中可以像在上面的随机“偏右”示例中一样高达 O(N)。如果我们调用 Remove(FindMax()),即我们删除当前的最大整数,我们将从根节点下降到最后一个叶节点,然后在 O(N) 时间内删除它(当其频率为 1 时)——这并不高效。

🕑

为了在'探索模式'中更轻松地操作,您可以使用以下选项创建新的BST:

  1. BST(然后您可以逐个插入几个整数),
  2. 您可能已经看过几次的一些e-讲座示例
  3. 随机 BST(不太可能非常高),
  4. 偏左/右 BST(具有N个顶点和N-1个类似链表的边的高BST,以展示BST操作的最坏情况行为;在AVL树模式中禁用)。
🕑

我们正在解释这个BST模块的过程中。到目前为止,我们注意到许多基本的表ADT操作在O(h)中运行,h可以像显示的 '偏左' 示例那样高达N-1条边 —— 效率低 :(...


那么,有没有办法使我们的BST '不那么高'?


附言:如果你想学习这些基本BST操作在真实程序中是如何实现的,你可以下载这个BSTDemo.cpp | py | java

🕑

此时,我们建议您按 [Esc] 或单击此e-Lecture幻灯片右下角的X按钮进入“探索模式”并自行尝试各种BST操作,以加强您对这种多功能数据结构的理解。
当您准备继续阅读平衡BST(以AVL树为示例)时,再次按 [Esc] 或从右上角的下拉菜单中将模式切换回“电子演讲模式”。 然后,使用幻灯片选择器下拉列表this slide 12-1恢复。

🕑
我们从之前的幻灯片中看到,除了中序遍历之外,我们的大部分BST操作都在 O(h) 中运行,其中h是BST的高度,可以与 N-1 一样高。
我们将继续讨论平衡BST的概念,以使得 h = O(log N)。
🕑
有几种已知的平衡BST的实现,但是太多了不能在VisuAlgo中逐一可视化和解释。
我们专注于AVL Tree(Adelson-Velskii&Landis,1962),以其发明者Adelson-Velskii和Landis命名。
其他平衡的BST实现(或多或少好于常数因子c)是:红黑树,B树/ 2-3-4树(Bayer&McCreight,1972),Splay树(Sleator 和Tarjan,1985),Skip Lists(Pugh,1989),Treaps( Seidel和Aragon,1996)等。
🕑
为了促进AVL Tree的实现,我们需要增加 - 为每个BST顶点添加更多信息/属性。
对于每个顶点v,我们定义height(v):从顶点v到其最深叶子的路径上的边数。 此属性保存在每个顶点中,因此我们可以在O(1)中访问顶点的高度,而无需每次都重新计算它。
🕑

正式公式是:

v.height = -1 (if v is an empty tree)
v.height = max(v.left.height, v.right.height) + 1 (otherwise)
因此,BST的高度是: root.height

在上面的例子BST上, height(11) = height(32) = height(50) = height(72) = height(99) = 0 (所有都是叶子)。height(29) = 1,因为有1个边将它连接到它唯一的叶子32上。

🕑

Quiz: What are the values of height(20), height(65), and height(41) on the BST above?

height(65) = 2
height(20) = 3
height(41) = 3
height(65) = 3
height(41) = 4
height(20) = 2
🕑

如果我们的BST中有N个元素/项目/键,如果我们能以完美的顺序插入N个元素,使BST完全平衡,那么下限高度h > log2 N


请参考上面的例子,N = 15(一个完美的BST,这在现实生活中很少能实现 - 尝试插入任何其他(不同的)整数,它就不再完美了)。

🕑
N ≤ 1 + 2 + 4 + ... + 2h
N ≤ 20 + 21 + 22 + … + 2h
N < 2h+1 (几何级数之和)
log2 N < log2 2h+1
log2 N < (h+1) * log2 2 (log2 2 is 1)
h > (log2 N)-1 (代数操作)
h > log2 N
🕑
如果我们在BST中有N个元素/项目/键,那么如果我们按升序插入元素,则上限高度 h < N(如上所示,右倾BST)。
这种BST的高度是 h = N-1,所以我们有
 h < N.
讨论:您知道如何左倾BST吗?

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑
我们已经看到大多数BST操作需要 O(h) 时间,并且结合h的下限和上限后会得到 log2 N < h < N
log2 N N 之间存在显着差异,我们从下限的讨论中看到,总是获得完美BST几乎不可能......
那么,对于一个小的常数因子c,我们可以得到高度接近 log2 N 的BST,即高度c * log2 N 的 BST 吗? 如果可以,那么需要O(h)运行的BST操作实际上只需 O(log N) 运行...

🕑
介绍AVL Tree,其早在1962年由两位俄罗斯(苏联)发明家 Georgy Adelson-Velskii和Evgenii Landis 发明。
在AVL树中,我们稍后会看到它的高度 h < 2 * log N(有更严谨的分析,但我们将在VisuAlgo中使用 c = 2 情况下的较简单的分析)。 因此,大多数AVL Tree操作以 O(log N) 时间高效运行。
插入(v)和删除(v)更新操作可能会更改AVL树的高度 h,但我们将看到旋转操作以保持较低的 AVL 树高度。
🕑
为了提高效率,每次有更新 (Insert(v)/Remove(v)) 操作时,我们都不会通过 O(N) 递归方法维护 height(v) 属性。
相反,我们在 Insert(v)/Remove(v) 操作的后面计算 O(1) 的:x.height = max(x.left.height, x.right.height) + 1,因为只有沿插入/移除路径的顶点的高度 (height) 可能会受到影响。 因此,只有 O(h) 顶点可以改变其 height(v) 属性,并且在AVL树中,h <2 * log N.
在示例AVL树上尝试Insert(37)(暂时忽略生成的旋转,我们将在接下来的几张幻灯片中回到它)。 请注意,沿插入路径只有几个顶点:{41,20,29,32}将高度增加+1,所有其他顶点的高度不变。
🕑
让我们定义以下重要的AVL树不变式(永不改变的属性):如果|v.left.height - v.right.height| ≤ 1,则顶点 v 被称为高度平衡
如果BST中的每个顶点都是高度平衡的,则根据上面的不变式此BST被称为高度平衡。 这样的BST称为AVL Tree,就像上面的例子一样。
在这里暂停一会,并尝试插入一些新的随机顶点或删除一些随机存在的顶点。 生成的BST是否仍然被认为是高度平衡的?
🕑
Adelson-Velskii 和 Landis 声称具有N个顶点的AVL树(满足AVL树不变式的高度平衡BST)具有高度 h < 2 * log2N
证明依赖于高度为 h 的最小 AVL 树。
令 Nh 为高度为h的AVL树中的最小顶点数。
Nh的前几个值是N0 = 1(单个根顶点),N1 = 2(只有一个左子或一个右子的根顶点),N2 = 4,N3 = 7,N4 = 12,N5 = 20(见背景图片),等等(见下两张幻灯片)。
🕑

我们知道,对于 N 个顶点的任何其他AVL树(不一定是最小尺寸的),N ≥ Nh

Proof-2

在背景图片中,我们有N5 = 20个顶点,但我们知道在我们有一个高度为h = 5的完美二叉树之前,我们可以再挤进43个顶点(最多N = 63)。

🕑
Nh = 1 + Nh-1 + Nh-2 (高度为h的最小大小AVL树的公式)
Nh > 1 + 2*Nh-2 (因为 Nh-1 > Nh-2)
Nh > 2*Nh-2 (显然)
Nh > 4*Nh-4 (递归)
Nh > 8*Nh-6 (另一步递归)
... (我们只能做这个h/2次,假设初始h是偶数)
Nh > 2h/2*N0 (我们达到基本情况)
Nh > 2h/2 (因为 N0 = 1)
证明-3
🕑
N ≥ Nh > 2h/2 (结合前两张幻灯片)
N > 2h/2
log2(N) > log2(2h/2) (两边都取 log2)
log2(N) > h/2 (公式简化)
2 * log2(N) > h 或 h < 2 * log2(N)
h = O(log(N)) (最终结论)
Proof-4
🕑

再看一下BST示例。 看到所有顶点都是高度平衡的AVL树。
为了快速检测顶点v是否高度平衡,我们将AVL树的(内部具有绝对函数的)不变式修改为:bf(v) = |v.left.height - v.right.height|
现在再次在AVL树上尝试Insert(37)。 插入路径上的几个顶点:{41,20,29,32}的高度增加1。 在插入之后顶点{29,20}将不再高度平衡了(并且将在稍后旋转 - 在接下来的几张幻灯片中讨论),i.e. bf(29)= -2和bf(20)= -2。 我们需要恢复平衡。

🕑
树旋转

看上面的图片。在左图上调用 rotateRight(D) 将产生右图。在右图上调用 rotateLeft(B) 将再次产生左图。


只有当 T 有左/右子节点时,才能调用 rotateRight(T)/rotateLeft(T)


树旋转 保留 BST 属性。
旋转前,A < B < C < D < E。
旋转后,注意以 C 为根的子树(如果存在)更换了父节点,
但 A < B < C < D < E 的顺序并未改变。

🕑
BSTVertex rotateLeft(BSTVertex T) // 先决条件:T的右子节点 T.right != null
BSTVertex w = T.right // 右旋是这个的镜像
w.parent = T.parent // 这个方法新手很难写对
T.parent = w
T.right = w.left
if (w.left != null) w.left.parent = T
w.left = T
// 更新 T 和 w 的高度 height
return w
🕑
四种情况

只有以下四种情况:

  1. 左左情况:bf(F) = +2 和 bf(D) = +1,解决方案:rotateRight(F)
  2. 左右情况:bf(F) = +2 和 bf(B) = -1,解决方案:先 rotateLeft(B) 将此情况转变为左左情况,然后转到步骤1
  3. 右右情况:bf(B) = -2 和 bf(D) = -1,解决方案:rotateLeft(B)
  4. 右左情况:bf(B) = -2 和 bf(F) = +1,解决方案:先 rotateRight(F) 将此情况转变为右右情况,然后转到步骤3
🕑
  1. 只需像普通BST一样插入v,
  2. 从插入点向上走AVL树回到根。每走一步,我们更新受影响顶点的高度和平衡因子:
    1. 如果有的话,停止在不平衡的第一个顶点(+2或-2),
    2. 使用四个树旋转案例中的一个来重新平衡它,例如 在上面的例子上尝试Insert(37)并通过调用 rotateLeft(29) 解决不平衡问题。

讨论:AVL Tree的 Insert(v) 操作是否还有其他树旋转情况?

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑
  1. 只需删除正常BST中的v(三个删除案例中的一个),
  2. 在AVL树种从删除点向上走回根节点,每一步,我们都会更新受影响的顶点的高度和平衡因子:
    1. 现在,对于每个不平衡的顶点(+2或-2),我们使用四个树旋转情况中的一个来重新平衡它们(可能多于一个)。

与AVL树中的 Insert(v) 相比的主要区别在于,我们可能会多次触发四种可能的重新平衡情况中的一种,但不会超过 h = O(log N) 次 :O。在上面的示例中尝试Remove(7),能看到连锁反应rotateRight(6) 然后rotateRight(16) + rotateLeft(8)

🕑

我们现在已经看到了AVL树如何定义高度平衡不变式,在对Insert(v)和Remove(v)更新操作期间对所有顶点进行维护,并且证明了AVL树的高度 h < 2 * log N


因此,所有二叉搜索树(BST)操作(包括更新和查询操作,除了中序遍历),如果它们的时间复杂度为O(h),则在使用AVL树版本的BST时,它们的时间复杂度为O(log N)。


这标志着本次电子讲座的结束,但请切换到“探索模式”,并尝试在AVL树模式下进行各种Insert(v)和Remove(v)调用,以加强您对这种数据结构的理解。


附言:如果您想学习这些看似复杂的AVL树(旋转)操作如何在实际程序中实现,您可以下载这个AVLDemo.cpp | java(必须与这个BSTDemo.cpp | java)一起使用。

🕑
在这个模块的结尾,我们将讨论一些关于BST和平衡BST(尤其是AVL树)的有趣事情。
🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.

🕑

关于这个数据结构的一些更有趣的问题,请在BST/AVL培训模块上进行练习(无需登录)。


然而,对于注册用户,您应该登录并从主页点击培训图标来正式完成这个模块,这样的成就将会被记录在您的用户账户中。

🕑

我们还有一些编程问题需要使用这种平衡的BST(如AVL Tree)数据结构:Kattis - compoundwordsKattis - baconeggsandspam
尝试使用它们来巩固和提高您对此数据结构的理解。 如果这样可以简化您的实现,则可以使用C ++ STL map / set或Java TreeMap / TreeSet。

尝试使用它们来巩固和提高您对此数据结构的理解。你可以使用 C++ STL map/set,Java TreeMap/TreeSet,或 OCaml Map/Set 来简化您的实现。请注意 Python 没有内置的平衡 BST 的实现。

🕑

The content of this interesting slide (the answer of the usually intriguing discussion point from the earlier slide) is hidden and only available for legitimate CS lecturer worldwide. This mechanism is used in the various flipped classrooms in NUS.


If you are really a CS lecturer (or an IT teacher) (outside of NUS) and are interested to know the answers, please drop an email to stevenhalim at gmail dot com (show your University staff profile/relevant proof to Steven) for Steven to manually activate this CS lecturer-only feature for you.


FAQ: This feature will NOT be given to anyone else who is not a CS lecturer.


You have reached the last slide. Return to 'Exploration Mode' to start exploring!

Note that if you notice any bug in this visualization or if you want to request for a new visualization feature, do not hesitate to drop an email to the project leader: Dr Steven Halim via his email address: stevenhalim at gmail dot com.

🕑

Toggle BST Layout

新建

插入(v)

移除(v)

前驱/后继(v)

选择

(v)

Traverse(root)

>

清空

Examples

N =

随机

Skewed

v =

前进

v =

前进

v =

找前驱

找后继

k =

前进

Inorder(root)

Preorder(root)

Postorder(root)

关于 团队 使用条款
隐私政策

关于

VisuAlgo最初由副教授Steven Halim于2011年构思,旨在通过提供自学、互动式学习平台,帮助学生更深入地理解数据结构和算法。

VisuAlgo涵盖了Steven Halim博士与Felix Halim博士、Suhendry Effendy博士合著的书《竞技编程》中讨论的许多高级算法。即使过去十年,VisuAlgo仍然是可视化和动画化这些复杂算法的独家平台。

虽然VisuAlgo主要面向新加坡国立大学(NUS)的学生,包括各种数据结构和算法课程(例如CS1010/等价课程,CS2040/等价课程(包括IT5003),CS3230,CS3233和CS4234),但它也是全球好奇心的宝贵资源,促进在线学习。

最初,VisuAlgo并不适用于智能手机等小触摸屏,因为复杂的算法可视化需要大量的像素空间和点击拖动交互。为了获得最佳用户体验,建议使用最低分辨率为1366x768的屏幕。然而,自2022年4月以来,VisuAlgo的移动(精简)版本已经推出,使得在智能手机屏幕上使用VisuAlgo的部分功能成为可能。

VisuAlgo仍然在不断发展中,正在开发更复杂的可视化。目前,该平台拥有24个可视化模块。

VisuAlgo配备了内置的问题生成器和答案验证器,其“在线测验系统”使学生能够测试他们对基本数据结构和算法的理解。问题根据特定规则随机生成,并且学生提交答案后会自动得到评分。随着越来越多的计算机科学教师在全球范围内采用这种在线测验系统,它可以有效地消除许多大学标准计算机科学考试中手工基本数据结构和算法问题。通过给通过在线测验的学生分配一个小但非零的权重,计算机科学教师可以显著提高学生对这些基本概念的掌握程度,因为他们可以在参加在线测验之前立即验证几乎无限数量的练习题。每个VisuAlgo可视化模块现在都包含自己的在线测验组件。

VisuAlgo已经被翻译成三种主要语言:英语、中文和印尼语。此外,我们还用各种语言撰写了关于VisuAlgo的公开笔记,包括印尼语、韩语、越南语和泰语:

id, kr, vn, th.

团队

项目领导和顾问(2011年7月至今)
Associate Professor Steven Halim, School of Computing (SoC), National University of Singapore (NUS)
Dr Felix Halim, Senior Software Engineer, Google (Mountain View)

本科生研究人员 1
CDTL TEG 1: Jul 2011-Apr 2012: Koh Zi Chun, Victor Loh Bo Huai

最后一年项目/ UROP学生 1
Jul 2012-Dec 2013: Phan Thi Quynh Trang, Peter Phandi, Albert Millardo Tjindradinata, Nguyen Hoang Duy
Jun 2013-Apr 2014 Rose Marie Tan Zhao Yun, Ivan Reinaldo

本科生研究人员 2
CDTL TEG 2: May 2014-Jul 2014: Jonathan Irvin Gunawan, Nathan Azaria, Ian Leow Tze Wei, Nguyen Viet Dung, Nguyen Khac Tung, Steven Kester Yuwono, Cao Shengze, Mohan Jishnu

最后一年项目/ UROP学生 2
Jun 2014-Apr 2015: Erin Teo Yi Ling, Wang Zi
Jun 2016-Dec 2017: Truong Ngoc Khanh, John Kevin Tjahjadi, Gabriella Michelle, Muhammad Rais Fathin Mudzakir
Aug 2021-Apr 2023: Liu Guangyuan, Manas Vegi, Sha Long, Vuong Hoang Long, Ting Xiao, Lim Dewen Aloysius

本科生研究人员 3
Optiver: Aug 2023-Oct 2023: Bui Hong Duc, Oleh Naver, Tay Ngan Lin

最后一年项目/ UROP学生 3
Aug 2023-Apr 2024: Xiong Jingya, Radian Krisno, Ng Wee Han

List of translators who have contributed ≥ 100 translations can be found at statistics page.

致谢
NUS教学与学习发展中心(CDTL)授予拨款以启动这个项目。在2023/24学年,Optiver的慷慨捐赠将被用来进一步开发 VisuAlgo。

使用条款

VisuAlgo慷慨地向全球计算机科学界提供免费服务。如果您喜欢VisuAlgo,我们恳请您向其他计算机科学学生和教师宣传它的存在。您可以通过社交媒体平台(如Facebook、YouTube、Instagram、TikTok、Twitter等)、课程网页、博客评论、电子邮件等方式分享VisuAlgo。

数据结构与算法(DSA)的学生和教师可以直接在课堂上使用本网站。如果您从本网站截取屏幕截图或视频,可以在其他地方使用,但请引用本网站的URL(https://visualgo.net)和/或下面的出版物列表作为参考。但请不要下载VisuAlgo的客户端文件并将其托管在您的网站上,因为这构成了抄袭行为。目前,我们不允许他人分叉此项目或创建VisuAlgo的变体。个人使用离线副本的客户端VisuAlgo是可以接受的。

请注意,VisuAlgo的在线测验组件具有重要的服务器端元素,保存服务器端脚本和数据库并不容易。目前,普通公众只能通过“培训模式”访问在线测验系统。“测试模式”提供了一个更受控制的环境,用于在新加坡国立大学的真实考试中使用随机生成的问题和自动验证。


出版物列表

这项工作曾在2012年国际大学生程序设计竞赛(波兰,华沙)的CLI研讨会上和2012年国际信息学奥林匹克竞赛(意大利,锡尔米奥内-蒙蒂基亚里)的IOI会议上展示过。您可以点击此链接阅读我们2012年关于该系统的论文(当时还没有称为VisuAlgo),以及此链接阅读2015年的简短更新(将VisuAlgo与之前的项目关联起来)。


错误报告或新功能请求

VisuAlgo并不是一个完成的项目。Steven Halim副教授仍在积极改进VisuAlgo。如果您在使用VisuAlgo时发现任何可视化页面/在线测验工具中的错误,或者您想要请求新功能,请联系Steven Halim副教授。他的联系方式是将他的名字连接起来,然后加上gmail dot com。

隐私政策

版本 1.2 (更新于2023年8月18日星期五)。

自2023年8月18日(星期五)起,我们不再使用 Google Analytics。因此,我们现在使用的所有 cookies 仅用于此网站的运营。即使是首次访问的用户,烦人的 cookie 同意弹窗现在也已关闭。

自2023年6月7日(星期五)起,由于 Optiver 的慷慨捐赠,全世界的任何人都可以自行创建一个 VisuAlgo 账户,以存储一些自定义设置(例如,布局模式,默认语言,播放速度等)。

此外,对于 NUS 学生,通过使用 VisuAlgo 账户(一个 NUS 官方电子邮件地址,课堂名册中的学生姓名,以及在服务器端加密的密码 - 不存储其他个人数据),您同意您的课程讲师跟踪您的电子讲义阅读和在线测验培训进度,这是顺利进行课程所必需的。您的 VisuAlgo 账户也将用于参加 NUS 官方的 VisuAlgo 在线测验,因此,将您的账户凭据传递给他人代您进行在线测验构成学术违规。课程结束后,您的用户账户将被清除,除非您选择保留您的账户(OPT-IN)。访问完整的 VisuAlgo 数据库(包含加密密码)的权限仅限于 Halim 教授本人。

对于全球其他已经给 Steven 写过信的 CS 讲师,需要一个 VisuAlgo 账户(您的(非 NUS)电子邮件地址,您可以使用任何显示名称,以及加密密码)来区分您的在线凭据与世界其他地方。您的账户将具有 CS 讲师特定的功能,即能够查看隐藏的幻灯片,这些幻灯片包含了在隐藏幻灯片之前的幻灯片中提出的问题的(有趣的)答案。您还可以访问 VisuAlgo 在线测验的 Hard 设置。您可以自由地使用这些材料来增强您的数据结构和算法课程。请注意,未来可能会有其他 CS 讲师特定的功能。

对于任何拥有 VisuAlgo 账户的人,如果您希望不再与 VisuAlgo 工具有关联,您可以自行删除您的账户。