给定一个带整数的二叉树,Left&右指针,如何在O(n)时间和O(1)额外内存(没有堆栈/队列/递归)中遍历树?
This guy给出了一个解决方案,该解决方案不是将当前路径编码为整数的O(n)总时间(因此适用于深度有限的树)。
我正在寻找经典解决方案
(SPOILER)
编码子节点中每个节点的父节点。
答案 0 :(得分:6)
任何好的算法手册都会有这个算法,例如在Knuth(TAOCP I.2.3.1遍历二叉树,练习21)。但是,由于此算法会修改树,因此必须在多线程环境中使用 extreme 警告。
您也可以使用线程树(参见Knuth)。
答案 1 :(得分:3)
主要思想类似于列表反转算法,基于指针(在人类目前已知的所有语言中)的事实,有一个超级丑陋的黑客攻击(从理论的角度来看,可能是作弊) ,0模式4为整数。
这个想法是你可以将树上路径上的指针翻转到指向上方。问题是 - 当你离开时,这就是你从列表反演算法中转移的地方 回溯你需要知道左点或右点是否向上;那时我们使用黑客。
伪代码如下:
current = root->left
next = current
while (current != null) {
next = current->left
current->left = static_cast<int>(prev) + 1 // ugly hack.
current = next
}
status = done
while (current != root or status != done) {
if (status = done) {
if (static_cast<u32>(current->left) %4 = 1) {
next = static_cast<u32>(current->left) -1
current->left = prev
status = middle
}
else {
next = current->right
current->right = prev
status = done
}
prev = current
current = next
}
else if (status == left) {
if (current->left) {
prev = current->left
current->left = static_cast<u32>(next) +1
next = current
}
else
status = middle
}
else if (status == right) {
if (current->right) {
prev = current->right;
current ->right = next;
next = current
}
else
status = done
}
else {// status == middle
work_on_node(current)
status = right
}
}
答案 2 :(得分:1)
那家伙的算法很有意思,但需要指出的是,它需要O(log n)额外的位空间来遍历具有n个节点的二叉树。空间要求必须以位而不是字节来衡量 - 通常在使用Big Oh表示法时它们会崩溃成相同的东西,但是这样的情况指出了为什么区分这一点很重要。
要看到这一点,请询问如何使用单个整数存储(在32位平台上)遍历具有2 ^ 32-1个节点的树。
答案 3 :(得分:0)
使用O(1)存储来记住“最后一个节点访问”指针。您可以将其初始化为0或某个未知值。
要遍历树,请从根节点开始。 查看节点的两个子节点。如果“上次访问的节点”等于RIGHT节点,则转到父节点。如果“上次访问”等于LEFT节点,则步进到右侧节点。其他步骤到左节点。
重复直到你走完整棵树。 唯一真正的聪明之处在于使用一个变量来记住你来自哪里,以便决定下一步该去哪里。这使得遍历具有确定性。
你最终会采取O(n)步骤。您将访问每个中间节点三次,每个叶子一次,所以您仍然是O(N)。存储是O(1)。
答案 4 :(得分:0)
这是另一种解决方案
http://sites.google.com/site/debforit/efficient-binary-tree-traversal-with-two-pointers
但是我想知道是否有一种方法可以在像Java那样没有真正意义上的指针的语言中做到这一点。