Algorithm 二叉树中的无堆栈预序遍历

Algorithm 二叉树中的无堆栈预序遍历,algorithm,Algorithm,是否可以在不使用节点堆栈或“已访问”标志的情况下在二叉树上执行迭代的*预排序*遍历 据我所知,这种方法通常要求树中的节点具有指向其父节点的指针。现在,可以肯定的是,我知道如何使用父指针和访问标志执行预顺序遍历,从而消除了迭代遍历对节点堆栈的任何要求 但是,我想知道是否真的有必要。如果树有很多节点,它们将占用大量内存。此外,如果一个二叉树的许多预排序树遍历同时进行,那么拥有它们也没有多大意义 如果可以执行,一些伪代码或更好的短C++代码示例将非常有用。p> 编辑:我特别不想使用递归进行预序遍历。我

是否可以在不使用节点堆栈或“已访问”标志的情况下在二叉树上执行迭代的*预排序*遍历

据我所知,这种方法通常要求树中的节点具有指向其父节点的指针。现在,可以肯定的是,我知道如何使用父指针和访问标志执行预顺序遍历,从而消除了迭代遍历对节点堆栈的任何要求

但是,我想知道是否真的有必要。如果树有很多节点,它们将占用大量内存。此外,如果一个二叉树的许多预排序树遍历同时进行,那么拥有它们也没有多大意义

如果可以执行,一些伪代码或更好的短C++代码示例将非常有用。p> 编辑:我特别不想使用递归进行预序遍历。我的问题的背景是,我在GPU上构建了一个八叉树(类似于二叉树)。我想启动许多线程,每个线程独立并行地遍历一棵树

首先,CUDA不支持递归。
通常,访问标志的概念仅适用于单个遍历。由于许多遍历同时进行,所以访问节点数据结构中的flags字段是没有用的。它们只有在CPU上才有意义,因为所有独立的树遍历都是/可以序列化的。更具体地说,在每次树遍历之后,我们可以在执行另一次预排序树遍历之前将访问标志设置为false

您可以在每个节点上添加一个位,指示第一个子分支的添加是向左还是向右。。。然后,通过遍历树可以选择每个分支的原始方向。

您可以给每个叶节点一个指针,指向根据前序遍历将进入的下一个节点

例如,给定二叉树:

          A
         / \
        B   C
       / \
      D   E
           \
            F
D需要存储一个指向E的指针,F需要存储一个指向C的指针。然后,您可以像遍历链表一样迭代遍历树


通过将同一指针同时存储在左侧和右侧子树节点中,无需额外存储即可完成此操作。由于树中不允许使用这种结构(这将使其成为DAG),因此可以安全地推断所有“子”指针指向同一位置的任何节点都是叶节点。

您可以使用此算法,该算法只需要父指针,无需额外存储:

对于内部节点,预顺序遍历中的下一个节点是其最左边的子节点

对于叶节点:继续在树中向上移动,直到从一个节点的左子节点开始,该节点有两个子节点。该节点的右子节点将成为下一个要遍历的节点

函数下一个节点(节点):
#内部节点:返回最左边的子节点
如果node.left!=无效的:
返回node.left
如果node.right!=无效的:
返回节点。右
#叶节
while(node.parent!=null)
如果node==node.parent.left和node.parent.right!=无效的:
返回node.parent.right
node=node.parent
返回null#不再有节点

如果您坚持这样做,您可以对树中所有可能的路径进行编号,然后将每个辅助进程设置为遵循该路径

您的编号方案可以是,每个零位表示取左子项,每个一位表示取右子项。要执行深度优先搜索,请将数字从最低有效位处理为最高有效位


虽然不必事先知道树的深度,但如果不知道,则需要处理以下情况:所有其他数字在完全消耗之前都会击中一个叶子。

有一种黑客使用{->left,->right}指针的绝对值对每个节点编码一位。它需要第一次传递,以获得正确的初始指针-“极性”。 它似乎被称为DSW。 你可以在这个usenet线程中找到更多信息


我不知道它是否可以扩展到四叉树或oct树,我严重怀疑它是否可以扩展到多线程访问。添加父指针可能更容易…

你可能要考虑的一个方向是在遍历它们时删除树的节点并将这些节点插入到新树中。如果在“预排序”中插入节点,则新树将完全相同。但这里的问题是如何在删除项时保持原始树的完整性。

递归是否算作使用堆栈?谢谢您指出这一点。请参见编辑。我也不想使用递归。为什么不能为访问的标志(即哈希表,访问后添加节点)使用外部数据结构?树是否包含每个节点的信息,其子节点中哪个是“左”哪个是“右”?@max yes,每个树节点都包含此信息。我认为每个标志添加1位正是他想要避免的存储类型。我理解OP旨在避免“访问标志”,这不是我在这里的意思。这些单位标志只是指示原始插入顺序。此外,一个位的价格也不高,是吗?@smilingfoddha,你刚才在这个注释插入上比我快:)使用父指针相当于额外的内存,不是吗?相反,您可以存储“要访问的下一个预订单节点”。这将使算法更加简单:p接下来:思考一下。常规的前序遍历需要在堆栈中存储O(log(n))指针(树的每一级都有一个指针)。您的算法需要O(n)个额外的指针(每个节点一个)。伊姆霍,这不是一个“正确”的解决方案。@Elkamina:是的,但父指针还有其他用途。如果您只需要预顺序遍历,那么我同意有更多内存效率更高的方法。