【问题标题】:Convert recursive binary tree traversal to iterative将递归二叉树遍历转换为迭代
【发布时间】:2011-11-24 18:29:00
【问题描述】:

我被要求写迭代版本,但我写的是递归版本,即

void inorderTraverse(BinaryTree root)
{
    if(root==NULL)
        printf("%d",root->id);
    else
    {
        inorderTraverse(root->left);
        printf("%d",root->id);
        inorderTraverse(root->right);
    }
}

我不是在寻找代码,我想了解如何做到这一点。如果这只是最后一次递归调用,我会这样做

void inorderTraverse(BinaryTree root)
{
    while(root!=NULL)
    {
        printf("%d",root->id);
        root=root->right;
    }
}

但是当有两个递归调用时如何转换为迭代程序?

这里是类型定义。

struct element{
    struct element* parent;
    int id;
    char* name;
    struct element* left;
    struct element* right;
};
typedef element* BinaryTree;

这是我想到的,我是不是走对了路?

temp=root;
while(1)
{
    while(temp!=NULL)
    {
     push(s,temp);
     temp=temp->left;
     continue;
    }

    temp=pop(s);
    if(temp==NULL)
    return;
    printf("%d\t",temp->data);
    temp=temp->right;
}

【问题讨论】:

标签: c recursion iteration binary-tree imperative-programming


【解决方案1】:

您看到的问题是您需要“记住”您进行迭代的最后一个位置
进行递归时,程序内部使用“堆栈”来记住要返回的位置。
但是在进行迭代时,它不会。

虽然……这给你一个想法吗?

【讨论】:

  • 我知道它与将东西压入堆栈然后弹出它们有关,但我真的无法弄清楚这一点,例如要推送什么,何时弹出以及如何弹出。
  • 好的,我可以从推送所有节点开始,直到我到达最左边的叶子,然后我开始弹出,现在在弹出第一个之后,我将右子节点推到 stach 上,并继续推直到我到达这个子树的最左边的叶子,现在我去右边的树,并到达那个子树的最右边的叶子,但是现在我怎么知道我需要做多少pops才能到达父级原树最左边的叶子?
  • 如果可以的话,请在堆栈方面帮助我更多。
  • @Karan:我不确定我是否完全按照你提到的,但你想做的是这样做:(1)​​推送当前节点并继续推送它的左子节点尽可能多次(即直到没有左节点),(2)弹出最后一个节点(这有返回值),(3)对弹出的最后一个节点的右子节点(如果有)重复步骤 1。
  • @Karan:我不知道为什么你的代码中有if(temp==NULL)——你从不推送NULL,那么为什么要弹出NULL?或者这应该表明空虚状态?但除此之外,看起来还不错……你试过了吗?
【解决方案2】:

我想不出一种真正优雅的方式来迭代地执行此操作。

一种可能性可能是使用“标记算法”,您从所有节点开始“未标记”并在处理时“标记”节点。标记可以添加到对象模型中或保存在单独的实体中。

伪代码:

for (BinaryTree currentNode = leftmostNode(root); currentNode != null; currentNode = nextNode(currentNode)):
  print currentNode;
  currentNode.seen = true;

sub nextNode(BinaryTree node):
  if (!node.left.seen):
    return leftmostNode(node.left)
  else if (!node.seen)
    return node
  else if (!node.right.seen)
    return leftmostNode(node.right)
  else 
    return nextUnseenParent(node)

sub leftmostNode(BinaryTree node):
  while (node.left != null)
    node = node.left
  return node;

sub nextUnseenParent(BinaryTree node):
  while (node.parent.seen)
    node = node.parent
  return node.parent

【讨论】:

  • 我也打算这样做,但这确实算作迭代版本,对吧?
  • 另外,我不认为这是一个中序遍历,事实上,你是从根开始打印的。
  • “迭代步骤”比平常复杂一点,但它只包含for 循环、while 循环并且没有递归调用,所以我认为它应该符合迭代的条件。
【解决方案3】:

我认为这是理所当然的,从父节点到左节点迭代不是问题。问题是要知道从一个节点上升到父节点时该怎么做:你应该选择正确的子节点还是应该再上升一个父节点?

以下技巧会帮助你:

在向上之前记住当前节点。然后往上走。现在你可以比较一下: 你有没有在左边的节点: 然后取右边的节点。否则再上一个父节点。

您只需要一个引用/指针。

【讨论】:

    【解决方案4】:

    有一种通用的方法可以将递归遍历转换为迭代器,方法是使用连接多个迭代器供应商的惰性迭代器(返回迭代器的 lambda 表达式)。见我的Converting Recursive Traversal to Iterator

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-19
      • 2019-12-01
      相关资源
      最近更新 更多