Algorithm 不使用额外内存的级别顺序树遍历

Algorithm 不使用额外内存的级别顺序树遍历,algorithm,data-structures,tree,tree-traversal,Algorithm,Data Structures,Tree,Tree Traversal,我知道树的水平顺序遍历的算法。(我想大家都知道)该算法使用队列存储树的节点。有没有不使用额外内存的算法?该算法不能使用递归(这样我们就可以使用堆栈)。注意,该树以左子右同级表示形式给出。不允许使用其他指针。 对于树,C中的结构是: struct node { int data; struct node *left-child; struct node *right-sibling; } 树用指向根节点的指针表示。当然,根不能有正确的同级。如果可以在树的每个节点中存储一个额外的下一个指针,该指针

我知道树的水平顺序遍历的算法。(我想大家都知道)该算法使用队列存储树的节点。有没有不使用额外内存的算法?该算法不能使用递归(这样我们就可以使用堆栈)。注意,该树以左子右同级表示形式给出。不允许使用其他指针。
对于树,C中的结构是:

struct node {
int data;
struct node *left-child;
struct node *right-sibling;
}

树用指向根节点的指针表示。当然,根不能有正确的同级。

如果可以在树的每个节点中存储一个额外的下一个指针,该指针按每个级别的级别顺序指向下一个节点,则可以在常量空间中进行级别顺序遍历

如果要在恒定空间中遍历树,可以应用Morris级别顺序遍历。可以引用和。

一种方法是使用空的
右同级
指针,使所有节点彼此成为同级(暂时)

你可以用一个又慢又快的指针。最快的一个总是在最后一个同级(它的空指针为
右同级
)。然后,慢速节点的
左子节点
指针将被复制到该
右同级节点
,之后快速指针将再次运行到末端。慢速指针向右移动一步,重复相同的操作。当慢速指针也到达终点时,所有节点都将是同级节点。可以使用慢速或快速指针按级别顺序输出值。这将完成任务,但树将因此被摧毁

要恢复树,我建议在上述过程中,所有兄弟边的方向都是反向的。这意味着您需要另一个落后于慢速指针的指针。这将允许在这两者之间执行反转。这有点晦涩,因为
右同胞
实际上会指向一些主要是左同胞的东西

在上面的过程之后,指针将位于节点列表的末尾,但是由于我们已经反转了同级边,所以我们也可以返回并再次反转边。一个困难是知道哪些同级指针应该再次变为null(当节点最初是最右边的子节点时)。这可以通过再次让一个快速指针向前移动(向左)来查找具有子节点的节点来实现。如果落后于慢指针的指针命中这样一个子指针,我们知道慢指针的节点应该得到一个空指针,即
右同级
。当应用此修复程序时,快速指针应再次向前运行,以查找另一个父节点等

请注意,此算法不会更改
左子指针

因此,这个解决方案总共使用了三个指针和树本身的结构

以下是我在以下实现中使用的示例树:

          1
         /
        2 ------------ 3 ---------4
       /              /          /
      5 -- 6 -- 7    8 -- 9     10 -- 11 -- 12 -- 13
          /                           /
        14 -- 15 -- 16              17 -- 18 -- 19
JavaScript中的实现--可运行代码段:

函数*遍历(节点){
让lead=node;//…走在node前面的某个地方
让lag=null;//…始终落后于节点一步
while(节点){
yield node.data;//输出
lead.rightSibling=node.leftChild;
而(lead.rightSibling)lead=lead.rightSibling;
//旋转:将节点指向下一个右同级,并反转同级边的方向
[node.rightSibling,lag,node]=[lag,node,node.rightSibling]
}
//还原树
lead=node=lag.rightSibling;//向后
lag.rightSibling=null;
而(lead!==null&&lead.leftChild==null)lead=lead.rightSibling;//实际上向左走!
while(节点){
if(lead!==null&&lead.leftChild==lag){
//当lag是某个节点(lead)的leftChild时,则lag不应成为rightSibling的目标
[node.rightSibling,lag,node]=[null,node,node.rightSibling];
//查找以前的父对象
lead=lead.rightSibling;
而(lead!==null&&lead.leftChild==null)lead=lead.rightSibling;//实际上向左走!
}否则{
//返回并恢复同级指针
[node.rightSibling,lag,node]=[lag,node,node.rightSibling];
}
}
}
//创建节点,给定其数据和子节点
功能节点(数据,…子节点){
//把孩子们当作兄弟姐妹联系起来
如果(children.length>1)children.reduceRight((a,b)=>(b.rightSibling=a,b))
//创建节点本身。目前,没有任何兄弟节点
返回{
数据,
leftChild:childrength.length?children[0]:null,
rightSibling:空
};
}
//示例树
设tree=Node(1,
节点(2,
节点(5),节点(6,
节点(14)、节点(15)、节点(16)
),节点(7)
),节点(3,
节点(8),节点(9)
),节点(4,
节点(10),节点(11,
节点(17)、节点(18)、节点(19)
),节点(12),节点(13)
)
);
//应用算法并输出生成的值

log(…遍历(树))我没有提到树是以左-子-右-同级表示形式给出的,不允许使用额外的指针。好吧,如果树是数组中表示的完整树,那么几乎不需要做任何事情:请说明树是如何表示的…(很好,更改为非二叉树)另请参见:@greybeard查看我在Rudresh Dixit的回答中所写的内容是否允许在遍历过程中修改树指针?如果是这样,一旦遍历完成,树必须恢复到其原始状态吗?这很有帮助,但我需要一些指导。我不知道如何将该算法(适用于二叉树)应用于普通树。每个(普通)树都可以用二叉树表示,但相应二叉树的级别顺序与(普通)树的级别顺序不同