将平衡二叉树复制到具有最小旋转的 AVL 树的最佳 'order' 遍历
Best 'order' traversal to copy a balanced binary tree into an AVL tree with minimum rotations
我有两棵二叉树。第一,A
我可以访问它的节点和指针(left
、right
、parent
)和 B
我无法访问其中任何一个它的内部结构。这个想法是通过遍历 A
的节点并将 insert
复制到 B
来复制 A
到 B
。 B
是一棵AVL树,在A
上是否有遍历(前序,中序,后序)使得向B
插入元素时有最小的旋转次数?
编辑:
- 树
A
是平衡的,只是不知道具体实现;
- 树
A
上的迭代需要仅使用指针来完成(编程语言是 C 并且没有我可以利用的队列或堆栈数据结构)。
当树的一部分的深度超过树的其他部分的深度不止一时,AVL 中的重新平衡就会发生。因此,为了避免触发重新平衡,您希望一次将节点一层级地馈送到 AVL 树中;也就是说,在向它提供 N+1 级的任何节点之前,先将原始树的 N 级的所有节点提供给它。
该排序将通过原始树的广度优先遍历来实现。
编辑
OP 添加:
Iteration on tree A needs to be done using only pointers (the
programming language is C and there is no queue or stack data
structure that I can make use of).
这并不影响所提出问题的答案,仍然是广度优先遍历需要最少的重新平衡。
它确实会影响您实现广度优先遍历的方式。如果您不能使用预定义的队列,那么有几种方法可以在 C 中实现您自己的队列:数组(如果允许)或某种链表是显而易见的选择。
如果您不允许使用动态内存分配,并且原始树的大小不受限制,您可以使用针对最坏情况调整大小的固定缓冲区来构建队列,那么您可以放弃基于队列的方法,而是使用递归来连续访问树的更深层次。 (想象一个递归遍历,当它到达树中的指定深度时停止,并且只为该指定深度的节点发出结果。将该递归包装在从运行的 while
或 for
循环中深度为零到树的最大深度。)
如果原始树不一定是AVL平衡的,那么你不能直接复制它。
为了保证新树不会再平衡,你应该创建一个完整的二叉树,并且你应该按照BFS/level的顺序插入节点,这样每棵中间树也都完成了。
一棵 "complete" 树是其中每一层都已满的树,可能除了最后一层。由于每棵完整的树都是 AVL 平衡的,并且每棵中间树都是完整的,因此不需要重新平衡。
如果您无法将原始树复制到数组或其他数据结构中,那么您将需要对原始树执行 log(N) 次顺序遍历以复制所有节点。在第一次遍历期间,您 select 并复制根。在第二个期间,你 select 并复制第 2 级。在第三个期间,你复制第 3 级,等等
源节点是否为每个级别 selected 仅取决于其在源树中的索引,因此与源树的实际结构无关。
由于每次遍历需要O(N)的时间,所以总的遍历时间为O(N log N)。但是,由于插入需要 O(log N) 时间,所以插入也需要多长时间,所以进行 log N 遍历不会增加整个过程的复杂性。
我有两棵二叉树。第一,A
我可以访问它的节点和指针(left
、right
、parent
)和 B
我无法访问其中任何一个它的内部结构。这个想法是通过遍历 A
的节点并将 insert
复制到 B
来复制 A
到 B
。 B
是一棵AVL树,在A
上是否有遍历(前序,中序,后序)使得向B
插入元素时有最小的旋转次数?
编辑:
- 树
A
是平衡的,只是不知道具体实现; - 树
A
上的迭代需要仅使用指针来完成(编程语言是 C 并且没有我可以利用的队列或堆栈数据结构)。
当树的一部分的深度超过树的其他部分的深度不止一时,AVL 中的重新平衡就会发生。因此,为了避免触发重新平衡,您希望一次将节点一层级地馈送到 AVL 树中;也就是说,在向它提供 N+1 级的任何节点之前,先将原始树的 N 级的所有节点提供给它。
该排序将通过原始树的广度优先遍历来实现。
编辑
OP 添加:
Iteration on tree A needs to be done using only pointers (the programming language is C and there is no queue or stack data structure that I can make use of).
这并不影响所提出问题的答案,仍然是广度优先遍历需要最少的重新平衡。
它确实会影响您实现广度优先遍历的方式。如果您不能使用预定义的队列,那么有几种方法可以在 C 中实现您自己的队列:数组(如果允许)或某种链表是显而易见的选择。
如果您不允许使用动态内存分配,并且原始树的大小不受限制,您可以使用针对最坏情况调整大小的固定缓冲区来构建队列,那么您可以放弃基于队列的方法,而是使用递归来连续访问树的更深层次。 (想象一个递归遍历,当它到达树中的指定深度时停止,并且只为该指定深度的节点发出结果。将该递归包装在从运行的 while
或 for
循环中深度为零到树的最大深度。)
如果原始树不一定是AVL平衡的,那么你不能直接复制它。
为了保证新树不会再平衡,你应该创建一个完整的二叉树,并且你应该按照BFS/level的顺序插入节点,这样每棵中间树也都完成了。
一棵 "complete" 树是其中每一层都已满的树,可能除了最后一层。由于每棵完整的树都是 AVL 平衡的,并且每棵中间树都是完整的,因此不需要重新平衡。
如果您无法将原始树复制到数组或其他数据结构中,那么您将需要对原始树执行 log(N) 次顺序遍历以复制所有节点。在第一次遍历期间,您 select 并复制根。在第二个期间,你 select 并复制第 2 级。在第三个期间,你复制第 3 级,等等
源节点是否为每个级别 selected 仅取决于其在源树中的索引,因此与源树的实际结构无关。
由于每次遍历需要O(N)的时间,所以总的遍历时间为O(N log N)。但是,由于插入需要 O(log N) 时间,所以插入也需要多长时间,所以进行 log N 遍历不会增加整个过程的复杂性。