是否总是可以使用树旋转将一个BST转换为另一个BST?

时间:2012-12-25 04:37:18

标签: algorithm data-structures binary-search-tree tree-rotation

给定一组值,可能存在许多可以由这些值形成的不同的可能二进制搜索树。例如,对于值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包含一组值,是否始终可以将该BS​​T转换为任意其他BST以获得同一组值?例如,我们是否可以仅使用树轮转换将上述五个BST中的任何一个转换为任何其他BST?

2 个答案:

答案 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!),然后使用上面的算法将其拉到树的根部。此时,我们已将第一棵树变为具有以下属性的树:

  1. 第一个树的根节点是第二个树的根节点。
  2. 第一个树的右子树包含与第二个树的右子树相同的节点,但可能具有不同的形状。
  3. 第一个树的左子树包含与第二个树的左子树相同的节点,但可能具有不同的形状。
  4. 因此,我们可以递归地应用相同的算法,使左子树具有与第二棵树的左子树相同的形状,并使右子树具有与第二棵树的右子树相同的形状。由于这些左右子树每个必须严格不超过n个节点,因此通过我们的归纳假设,我们知道总是可以这样做,因此算法将按预期工作。

    总而言之,该算法的工作原理如下:

    1. 如果两棵树都是空的,我们就完成了。
    2. 在第一个树中查找第二个树的根节点。
    3. 应用轮换以将该节点提升到根目录。
    4. 递归地重塑第一棵树的左子树,使其具有与第二棵树的左子树相同的形状。
    5. 递归地重塑第一棵树的右子树,使其具有与第二棵树的右子树相同的形状。
    6. 要分析此算法的运行时间,请注意应用步骤1 - 3最多需要O(h)步骤,其中h是第一棵树的高度。每个节点都会被引导到某个子树的根部一次,所以我们总共执行O(n)次。由于n节点树的高度永远不会大于O(n),这意味着该算法最多需要O(n 2 )时间才能完成。它可能会做得更好(例如,如果两棵树已经具有相同的形状,那么它会在时间O(n)中运行),但这会给出一个很好的最坏情况。

      希望这有帮助!

答案 1 :(得分:2)

对于二进制搜索树,实际上可以在 O(n)中完成。

任何树都可以“理顺”,即放入一个所有节点都是根或左子节点的形式。

这种形式是独一无二的(从根读下来给出了元素的排序)

树木如下所示:

  • 对于任何正确的孩子,请对自己进行左转。这会减少 1 的正确子女数量,因此树在 O(n)轮换中被理顺。

如果A可以在 O(n)旋转中被拉直到S,并且在 O(n)旋转中可以将B拉直到S,那么由于旋转是可逆的,所以可以转A - >; S - &gt; B在 O(n)轮换中。