假设我有一个平衡的二叉搜索树来表示这个有序序列。
A<B<C<D<E<F<G<H
给定其中一个元素,例如F,如何有效地转换树,以便结果表示这个有序序列?
F<G<H<A<B<C<D<E?
从F到右边的元素被移动到所有其他元素的前面。请注意,这与通常意义上的“树旋转”无关。这里的旋转发生在元素的顺序意义上。它与双向链表的“旋转”含义相同。例如,如果问题是关于双链表而不是二进制搜索树,那么解决方案很简单:
E.next := null
F.prev := null
H.next := A
A.prev := H
平衡二叉搜索树是否有效的解决方案?
实施说明:
首先看起来似乎即使有一个有效的算法,它也没什么用,因为必须更新移动元素的值以保留二叉搜索树的不变量(左孩子较小,右孩子较大)。但是,情况并非如此,因为在模运算模 N 中,可以在不改变节点值的情况下在固定时间内固定顺序。假设节点的顺序定义如下:
(A < B) if and only if ((A.value - C) mod N) < ((B.value - C) mod N)
此处, A.value , B.value 和 C 是 [0,N]范围内的整数。对此的图形解释是我们有一个 N 点遍布的圆圈,我们对这些点进行排序,使 C 为最小点,然后是 C +1 , C + 2 等,最多 C +(N-1),这是最重要的一点。
无论如何,在将 F 和所有后续元素移到前面后,通过更改 C 可以轻松修复树不变量:
C := F.value
答案 0 :(得分:2)
通常,这不能在少于O(N)的时间内完成。经过一些小改动后,余额可以在O(log N)中恢复,但是切割整个分支并将其粘贴到其他地方是一个很大的变化。
提取n个元素并逐个插入它们需要O(n log N)。如果n很大,重建整个树是值得的,这可以在O(N)时间内完成。
也就是说,您可以将有序遍历的整个序列视为循环列表。您可以维护一个指向您认为是“第一个”元素的指针,只需要在将某些元素从“结束”移动到“开头”时更改它,反之亦然。如果要按顺序访问序列,只需从指向的元素(“第一个”)开始,继续按顺序遍历并环绕树的末尾。在访问最右边的元素后,继续最左边的元素。当你再次到达“第一个”元素时停止。