在C中没有递归和堆栈的遍历树

Posted

技术标签:

【中文标题】在C中没有递归和堆栈的遍历树【英文标题】:Traverse tree without recursion and stack in C 【发布时间】:2011-03-13 23:03:19 【问题描述】:

如何在C(非C++)中有效地遍历树的每个节点而不递归?

假设我有该树的以下节点结构:

struct Node

    struct Node* next;   /* sibling node linked list */
    struct Node* parent; /* parent of current node   */
    struct Node* child;  /* first child node         */

这不是家庭作业。 我更喜欢深度优先。 我更喜欢不需要额外的数据结构(例如堆栈)。 我更喜欢在速度方面最有效的方式(而不是空间)。 您可以更改或添加Node 结构的成员以存储更多信息。

【问题讨论】:

任意顺序,最好是深度优先 我编辑了问题标题(添加了“无堆栈”),以便在有人搜索特定需求时清楚说明 【参考方案1】:

如果您不想存储任何内容,并且可以使用深度优先搜索:

process = TRUE;
while(pNode != null) 
    if(process) 
        //stuff
    
    if(pNode->child != null && process) 
         pNode = pNode->child;
         process = true;
     else if(pNode->next != null) 
         pNode = pNode->next;
         process = true;
     else 
         pNode = pNode->parent;
         process = false;
    

将遍历树; process 是为了防止它在返回时重新命中父节点。

【讨论】:

+1 用于识别兄弟节点链表的工作方式,而不是像高分答案那样引入大量无意义的开销...... +1。这看起来是对的。因此我删除了我的答案:-)(我使用了额外的节点信息)。解释一下:这是不变量:访问节点时,如果 process 为真,则意味着您尚未遍历该子树。只有在访问完所有子节点后才能返回节点,在这种情况下,进程是错误的,您现在移动到您的兄弟节点。 @ShinTakezou:我们不要妄下结论。 布赖恩的答案发布后,该问题被编辑以禁止堆栈。 @Moron 我不想贸然下结论。由于堆栈本身,我没有更好地评估这一点。这看起来更好(对我来说!!),因为给出了实现目标的代码,给定了结构。另一个答案很好,但是“通用”;这个似乎更具体(哦,我希望他至少在一开始就给出了结构,并且他后来没有改变它!!) 修改的是我的措辞,而不是我的编码。它最初是“基于布莱恩的回答”,但布莱恩改变了他的,所以它不再相关【参考方案2】:

通常,您将使用自己的堆栈数据结构,该结构存储节点列表(或队列,如果您想要级别顺序遍历)。

您首先将任何给定的起始节点压入堆栈。然后你进入你的主循环,直到堆栈为空。从堆栈中弹出每个节点后,如果不为空,则推送其下一个节点和子节点。

【讨论】:

这也不会遍历树。 如果我使用链表实现自己的堆栈,会比使用递归更有效吗? @uray:我认为递归函数调用不会成为您程序的瓶颈。但是如果你有一个巨大的巨大的树,那么实现你自己的可能会更有效,因为你可以根据需要自己做一些事情,比如将堆栈的一部分缓存到磁盘。 是否有任何算法不使用任何迭代数据结构(如堆栈或队列),例如通过在该节点结构中修改或添加变量? 阅读 zebediah49 的回答。这里树结构的设计方式,解决起来很简单。【参考方案3】:

这看起来像是 25 年前我在工程学校做的一个练习。 我认为这被称为树包络算法,因为它绘制了树的包络。

我不敢相信事情就这么简单。我一定在某个地方犯了一个不经意的错误。 不管有什么错误,我相信包围策略是正确的。 如果代码有错误,则将其视为伪代码。

while current node exists
  go down all the way until a leaf is reached;
  set current node = leaf node;
  visit the node (do whatever needs to be done with the node);

  get the next sibling to the current node;
  if no node next to the current
    ascend the parentage trail until a higher parent has a next sibling;
  
  set current node = found sibling node;

代码:

void traverse(Node* node)

  while(node!=null)
    while (node->child!=null)
      node = node->child;
    

    visit(node);

    node = getNextParent(Node* node);
  


/* ascend until reaches a non-null uncle or 
 * grand-uncle or ... grand-grand...uncle
 */
Node* getNextParent(Node* node)

  /* See if a next node exists
   * Otherwise, find a parentage node
   * that has a next node
   */
  while(node->next==null)
    node = node->parent;
    /* parent node is null means
     * tree traversal is completed
     */
    if (node==null)
      break;
  

  node = node->next;

  return node;

【讨论】:

【参考方案4】:

您可以使用Pointer Reversal 方法。缺点是需要在节点内部保存一些信息,所以不能在const数据结构上使用。

【讨论】:

【参考方案5】:

您必须将其存储在可迭代列表中。带有索引的基本列表将起作用。然后,您只需从 0 到结束查看数据。

如果您想避免递归,您需要保留树中每个对象的引用。

【讨论】:

以上是关于在C中没有递归和堆栈的遍历树的主要内容,如果未能解决你的问题,请参考以下文章

无递归、无堆栈且不改变树的树遍历

递归非递归的二叉树遍历(递归前中后,非递归前中后,层次遍历,凹入打印法等)

非递归实现中序,先序,后序遍历二叉树部分代码

非递归实现中序,先序,后序遍历二叉树部分代码

java数据结构之二叉树遍历的非递归实现

二叉树的各种遍历方式,我都帮你总结了,附有队列堆栈图解