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

Algorithm 是否总是可以使用树旋转将一个BST转换为另一个BST?,algorithm,data-structures,binary-search-tree,tree-rotation,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 许多基于平衡二叉搜索树的数据结构使用作为原语来重塑BST,而不破坏所需的二叉搜索树不变量。树旋

给定一组值,可能会有许多不同的二进制搜索树,它们可以由这些值形成。例如,对于值1、2和3,我们可以从这些值生成五个BST:

1      1      2      3      3
 \      \    / \    /      /
  2      3  1   3  1      2
   \    /           \    /
    3  2             2  1
许多基于平衡二叉搜索树的数据结构使用作为原语来重塑BST,而不破坏所需的二叉搜索树不变量。树旋转可用于将节点拉到其父节点上方,如下所示:

                rotate
         u      right           v
        / \     ----->         / \
       v   C                  A   u
      / \       <-----           / \
     A   B      rotate          B   C
                 left
旋转
u右v
/ \     ----->         / \
v C A u

/\您的问题的答案取决于是否允许您在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。我们将证明,给定由相同n+1值构成的任意两个BST,总是可以将第一棵树转换为第二棵树

要做到这一点,我们首先要做一个关键的观察。给定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(n2)个时间来完成。它可能会做得更好(例如,如果两棵树已经具有相同的形状,那么这将在时间O(n)内运行),但这给出了一个很好的最坏情况界限


    希望这有帮助

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

    任何树都可以被“拉直”,即所有节点都是根或左子节点的形式

    此表单是唯一的(从根目录向下读取元素的顺序)

    一棵树被拉直如下:

    • 对于任何右子对象,围绕其自身执行左旋转。这会将右侧子级的数量减少1,因此树在O(n)中变直