给定一组值,可能存在许多可以由这些值形成的不同的可能二进制搜索树。例如,对于值1,2和3,我们可以从这些值中生成五个BST:
1 1 2 3 3
\ \ / \ / /
2 3 1 3 1 2
\ / \ /
3 2 2 1
许多基于平衡二叉搜索树的数据结构使用 tree rotations 作为重构BST的原语,而不会破坏所需的二进制搜索树不变量。树旋转可用于将节点拉到其父节点之上,如下所示:
rotate
u right v
/ \ -----> / \
v C A u
/ \ <----- / \
A B rotate B C
left
鉴于BST包含一组值,是否始终可以将该BST转换为任意其他BST以获得同一组值?例如,我们是否可以仅使用树轮转换将上述五个BST中的任何一个转换为任何其他BST?
答案 0 :(得分:18)
您的问题的答案取决于您是否被允许在BST中具有相同的值,这些值可能看起来彼此不同。例如,如果您的BST存储键/值对,那么对于相同的键/值对,并不总是可以将这些键/值对的一个BST转换为不同的BST。
这样做的原因是无论执行多少树旋转,BST中节点的顺序遍历都保持不变。因此,如果节点的顺序遍历会以不同方式出现,则无法从一个BST转换为另一个BST。作为一个非常简单的情况,假设您有一个BST,其中包含数字1的两个副本,每个副本都注释了不同的值(例如,A或B)。在这种情况下,无法使用树旋转将这两棵树相互转换:
1:a 1:b
\ \
1:b 1:a
您可以通过强制使用旋转强制(非常小!)可能的树组来检查这一点。但是,只需要注意第一个树的顺序遍历给出1:a,1:b并且第二个树的顺序遍历给出1:b,1:a。因此,没有多少轮换就足以在树之间进行转换。
另一方面,如果所有值都不同,则始终可以通过应用正确数量的树旋转来在两个BST之间进行转换。我将使用关于节点数量的归纳论证来证明这一点。
作为一个简单的基本情况,如果树中没有节点,则只有一个可能的BST保存这些节点:空树。因此,总是可以在两个树之间进行转换,其中包含零节点,因为开始和结束树必须始终相同。
对于归纳步骤,让我们假设对于具有相同值的0,1,2,...,n个节点的任何两个BST,总是可以使用旋转从一个BST转换为另一个BST。我们将证明,如果任何两个BST由相同的n + 1值组成,则始终可以将第一个树转换为第二个。
要做到这一点,我们首先要做一个关键的观察。给定BST中的任何节点,始终可以应用树旋转将该节点拉到树的根。为此,我们可以应用此算法:
while (target node is not the root) {
if (node is a left child) {
apply a right rotation to the node and its parent;
} else {
apply a left rotation to the node and its parent;
}
}
这样做的原因是,每次使用父节点旋转节点时,其高度都会增加1。结果,在应用了足够多的上述形式的旋转之后,我们可以将根到达树的顶部。
这现在为我们提供了一个非常简单的递归算法,我们可以使用旋转将任何一个BST重塑为另一个BST。这个想法如下。首先,查看第二个树的根节点。在第一棵树中找到该节点(这很简单,因为它是BST!),然后使用上面的算法将其拉到树的根部。此时,我们已将第一棵树变为具有以下属性的树:
因此,我们可以递归地应用相同的算法,使左子树具有与第二棵树的左子树相同的形状,并使右子树具有与第二棵树的右子树相同的形状。由于这些左右子树每个必须严格不超过n个节点,因此通过我们的归纳假设,我们知道总是可以这样做,因此算法将按预期工作。
总而言之,该算法的工作原理如下:
要分析此算法的运行时间,请注意应用步骤1 - 3最多需要O(h)步骤,其中h是第一棵树的高度。每个节点都会被引导到某个子树的根部一次,所以我们总共执行O(n)次。由于n节点树的高度永远不会大于O(n),这意味着该算法最多需要O(n 2 )时间才能完成。它可能会做得更好(例如,如果两棵树已经具有相同的形状,那么它会在时间O(n)中运行),但这会给出一个很好的最坏情况。
希望这有帮助!
答案 1 :(得分:2)
对于二进制搜索树,实际上可以在 O(n)中完成。
任何树都可以“理顺”,即放入一个所有节点都是根或左子节点的形式。
这种形式是独一无二的(从根读下来给出了元素的排序)
树木如下所示:
如果A可以在 O(n)旋转中被拉直到S,并且在 O(n)旋转中可以将B拉直到S,那么由于旋转是可逆的,所以可以转A - >; S - &gt; B在 O(n)轮换中。