【发布时间】:2019-11-15 13:20:37
【问题描述】:
我想知道是否可以在仅使用 O(1) 空间的情况下以广度一阶打印二叉树?
困难的部分是人们必须使用额外的空间来记忆下一个要遍历的级别,并且随着 n 的增长而增长。
由于我们没有对时间部分进行任何限制,也许有一些低效(就时间而言)的方式可以实现这一点?
有什么想法吗?
【问题讨论】:
-
@Ted 这是一个潜在的面试问题。
我想知道是否可以在仅使用 O(1) 空间的情况下以广度一阶打印二叉树?
困难的部分是人们必须使用额外的空间来记忆下一个要遍历的级别,并且随着 n 的增长而增长。
由于我们没有对时间部分进行任何限制,也许有一些低效(就时间而言)的方式可以实现这一点?
有什么想法吗?
【问题讨论】:
这将取决于一些更细粒度的定义,例如边缘是否有反向链接。然后就很容易了,因为您只需按照树上的反向链接即可。否则我想不出没有 O(lg number of nodes) 空间的方法,因为你至少需要记住“上面”的节点。
更新
哦等等,当然它可以在 O(1) space 中通过时空交易来完成。在任何你想做反向链接的地方,你保存你的位置并做 BFS,跟踪最近的节点,直到你找到你的。然后备份到最近访问的节点并继续。
问题是,这是 O(1) 空间但 O(n^2) 时间。
另一个更新
假设我们已经到达节点 n_i,并且我们想要到达那个节点的父节点,我们称之为 wlg n_j。我们已经确定了可区分的根节点 n_0。
修改呼吸优先搜索算法,以便当它遵循有向边 (n_x,n_y) 时,存储传出或“传入”节点。因此,当您关注 (n_x,n_y) 时,您会保存 n_x。
当您从 n_0 再次启动 BFS 时,您可以保证(假设它确实是一棵树)在某个点,您将转换边 (n_j,n_i)。那时你观察到你回到了 n_i。您已经存储了 n_j,因此您知道反向边缘是 (n_i,n_j)。
因此,您得到的回溯只有两个额外的单元格,一个用于 n_0,一个用于“已保存”节点。这是 O(1)
我不太确定 O(n^2) - 现在很晚,而且这是艰难的一天,所以我不想编写证明。我确定它是 O((|N|+|E|)^2) 其中 |N|和 |E|分别是顶点集和边集的大小。
【讨论】:
一个有趣的特殊情况是堆。
来自heapq docs:
堆是每个父节点的值小于的二叉树 大于或等于其任何子代。此实现使用数组 其中
heap[k] <= heap[2*k+1]和heap[k] <= heap[2*k+2]对于所有k, 从零开始计数元素。为了比较,不存在 元素被认为是无限的。一个有趣的属性 heap 是它的最小元素始终是根,heap[0]。 [弗朗索瓦·皮纳尔的解释]
一棵树在内存中的表示方式(数组的索引):
0
1 2
3 4 5 6
7 8 9 10 11 12 13 14
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
在这种情况下,数组中的节点已经按广度优先顺序存储。
for value in the_heap:
print(value)
在空间中 O(1)。
【讨论】:
我知道这严格来说不是问题的答案,但是可以使用 O(d) 空间来以广度优先顺序访问树的节点,其中 d 是树的深度,通过递归 iterative deepening depth first search (IDDFS)。当然,堆栈需要空间。在平衡树的情况下,d = O(lg n) 其中n 是节点数。老实说,如果没有@Charlie Martin 建议的反向链接,我真的不明白你会如何在恒定空间中做到这一点。
【讨论】:
很容易实现递归方法来获取给定级别的树的所有节点。因此,我们可以计算树的高度并获取所有节点和每个级别。这是树的Level Order Traversal。但是,时间复杂度是O(n^2)。下面是 Java 实现 (source)。
class Node
{
int data;
Node left, right;
public Node(int item)
{
data = item;
left = right = null;
}
}
class BinaryTree
{
Node root;
public BinaryTree()
{
root = null;
}
void PrintLevelOrder()
{
int h = height(root);
int i;
for (i=1; i<=h; i++)
printGivenLevel(root, i);
}
int Height(Node root)
{
if (root == null)
return 0;
else
{
int lheight = height(root.left);
int rheight = height(root.right);
}
}
void PrintGivenLevel (Node root ,int level)
{
if (root == null)
return;
if (level == 1)
System.out.print(root.data + " ");
else if (level > 1)
{
printGivenLevel(root.left, level-1);
printGivenLevel(root.right, level-1);
}
}
}
【讨论】: