迭代后订单遍历而不保持访问标志

时间:2009-08-29 02:57:52

标签: algorithm tree-traversal inorder preorder postorder

为什么有必要为迭代后序遍历保留访问标志,而不是为了顺序或预先迭代遍历。

是否可以在不保留访问标记的情况下进行邮购顺序?

6 个答案:

答案 0 :(得分:2)

可以在不使用访问标志的情况下实现后序遍历迭代版本,实现起来更加困难。

请参阅此处,了解迭代后序遍历的两种解决方案,而不使用任何访问过的标记。

http://www.leetcode.com/2010/10/binary-tree-post-order-traversal.html

答案 1 :(得分:1)

如果我们从简单的递归后序算法开始,

def postorder1(node, f):
  # a:
    if node is None:
        return None
    postorder1(node.left, f)
  # b:
    postorder1(node.right, f)
  # c:
    f(node)

我们可以将函数分为“a”,“b”和“c”三部分,然后通过模拟虚拟调用堆栈将其简单地转换为迭代算法:

def postorder2(node, f):
    stack = [("a", node)]
    while stack:
        go, node = stack.pop()
        if go == "a":
            if node is not None:
                stack.append(("b", node))
                stack.append(("a", node.left))
        elif go == "b":
            stack.append(("c", node))
            stack.append(("a", node.right))
        elif go == "c":
            f(node)

由此我们观察到堆栈只能以三种方式之一进行修改:

[…, a] → […, b, a]
[…, b] → […, c, a]
[…, c] → […]

这意味着:

  • “a”只能在堆栈顶部出现一次,而
  • “b”和“c”并且在堆栈中间出现任意次数,可能是交错的。

因此,我们并不需要在堆栈中存储“a” - 存储“a”的单个变量就足够了。这使我们能够将朴素的迭代算法简化为更传统的形式:

def postorder3(node, f):
    stack = []
    while True:
        if node:
            stack.append((True, node))
            node = node.left
        elif stack:
            left, top = stack.pop()
            if left:
                stack.append((False, top))
                node = top.right
            else:
                f(top)
        else:
            break

堆栈上的布尔标志是“访问标志”。在此示例中,我们不将标志直接存储在节点上,而是存储在堆栈中,但净效果是相同的。该算法的某些变体使用“最后访问过的节点”变量,但这需要一种比较节点“身份”(指针/引用相等)的方法,我们在此不假设。

此标志是算法的必要部分,因为如前面的分析中所述,堆栈中的“b”和“c”条目可以完全任意的方式出现。我们需要某些类型的信息告诉我们是否应该向右移动或致电f

答案 2 :(得分:0)

这是一次订单后访问:

postordervisit(t)
{   if not(leaf(t))
    { postordervisit(left(t);
      postordervisit(right(t));
    }
L1: process(t);
        L2:
}

它不使用任何标志。为什么你认为有必要举旗?

也许我不明白这句话,“迭代后订单遍历”。 通过对称性,如果您认为“迭代前序遍历”不需要标记, 我认为你必须相信“迭代后序遍历”也是 国旗免费,反之亦然。

编辑:我的坏,一定是深夜。 “迭代”意思是“没有递归的实现”。 好的,所以你实现了自己的节点堆栈,你必须实现相当于一堆返回地址的节点。我认为旗帜正在模拟回归的效果 知道下一步是去L1还是L2的地址。因为你需要这个用于下订单,所以你需要它来预订。

编辑2010年10月:我的又坏了,提供的算法不是下订单。修改。

答案 3 :(得分:0)

我相信先前发布的端口顺序遍历的算法显示在访问左子树之前“处理”节点。 Postorder Traversal与反向波兰表示法基本相同,其中操作数(叶节点或子树)位于运算符(下一个更高的子树节点)之前。

更正的后序遍历算法将是:

postordervisit(t)
{   if null(t) return;    
    postordervisit(right(t));
    postordervisit(left(t);
    process(t);
}

这将在访问子树的根之前访问叶子树或子树节点。

答案 4 :(得分:0)

我在用户 1337c0d3r 的解决方案中发现了一个问题:它只是一个反向顺序的预订单。我的解决方案使用“活动列表”来标记堆栈中的节点。

(您可以将标记标记存储在堆栈中。将单独发布该解决方案。)

void print_postorder(Nodes const& roots)
{
    typedef std::set<Node const*> ActiveList;
    ActiveList activeNodes;
    vector<Nodes::const_iterator> stack(1, roots.begin());

    while( stack.empty() == false )
    {
        Nodes::const_iterator node = stack.back();
        ActiveList::iterator activeEntry = activeNodes.find( &*node );

        if( activeEntry == activeNodes.end() )
        {
            // This node is now active.
            activeNodes.insert( &*node );
            // Plan to visit each child.
            for( Nodes::const_reverse_iterator rchild = node->children.rbegin();
                 rchild != node->children.rend(); ++rchild )
            {
                Nodes::const_reverse_iterator rchild2 = rchild;
                Nodes::const_iterator child = (++rchild2).base();
                stack.push_back(child);
            }
        }
        else
        {
            // Post-order visit the node.
            std::size_t depth = activeNodes.size();
            for( std::size_t i = 0; i < 4 * depth; ++i )
                cout << ' ';  // Indent
            cout << node->name << endl;
            // We're finished with this node.
            activeNodes.erase( activeEntry );
            stack.pop_back();
        }
    }
}

// Try this for yourself!  Tree representation:

#include <vector>
#include <set>

struct Node;
typedef std::vector<Node> Nodes;
struct Node
{
    std::string name;
    Nodes children;
};

答案 5 :(得分:0)

标记不是必需的,应该避免,因为读者不应该修改任何东西,例如,任何修改都不允许并发。 Here是C中作为宏的迭代后遍历的实现。它适用于具有适当配置的任何树类型,也可以执行相反的后顺序。整个库(还实现了迭代的预遍历)为here

#define W_TREE_FOR_EACH_POSTORDER(T,Child,self)                                                \
    W_DECLARE(W_CAT(Child,po1), T *Child)                                                      \
    W_DECLARE(W_CAT(Child,po2), T* W_ID(node) = (self))                                        \
    W_DECLARE(W_CAT(Child,po3), T** W_ID(stack) = NULL )                                       \
    W_DECLARE(W_CAT(Child,po9), int W_ID(_finish_) = 0 )                                       \
    if (W_ID(node) == NULL)                                                                    \
        ;                                                                                      \
    else                                                                                       \
        W_BEFORE(W_CAT(Child,po4), goto W_LABEL(6,Child); )                                    \
        while (!W_ID(_finish_))                                                                \
            W_BEFORE (W_CAT(Child,po5),                                                        \
              W_LABEL(6,Child):                                                                \
                while (W_ID(node)) {                                                           \
                    BOOST_PP_IF(W_REVERSED,                                                    \
                        W_TREE_FOR_EACH_IMMEDIATE_REVERSED(T,W_CAT(Child,_child), W_ID(node))  \
                            if (W_CAT(Child,_child,_ix) < W_TREE_GET_DEGREE(W_ID(node))-1)     \
                                W_DYNAMIC_STACK_PUSH( W_ID(stack),W_CAT(Child,_child) );       \
                        W_DYNAMIC_STACK_PUSH( W_ID(stack),W_ID(node) );                        \
                        W_ID(node) = W_TREE_NEXT_RIGHTMOST(W_ID(node));                        \
                        , /* else */                                                           \
                        W_TREE_FOR_EACH_IMMEDIATE(T,W_CAT(Child,_child), W_ID(node))           \
                            if (W_CAT(Child,_child,_ix) > 0)                                   \
                                W_DYNAMIC_STACK_PUSH( W_ID(stack),W_CAT(Child,_child) );       \
                        W_DYNAMIC_STACK_PUSH( W_ID(stack),W_ID(node) );                        \
                        W_ID(node) = W_TREE_NEXT_LEFTMOST(W_ID(node));                         \
                    )                                                                          \
                }                                                                              \
                W_ID(node) = W_DYNAMIC_STACK_POP( W_ID(stack) );                               \
                BOOST_PP_IF(W_REVERSED,                                                        \
                    if (W_ID(node) && W_TREE_NEXT_LEFTMOST(W_ID(node))                         \
                        && W_DYNAMIC_STACK_PEEK_SAFE(W_ID(stack),NULL) == W_TREE_NEXT_LEFTMOST(W_ID(node)) ) { \
                        W_DYNAMIC_STACK_POP(W_ID(stack));                                      \
                        W_DYNAMIC_STACK_PUSH( W_ID(stack),W_ID(node) );                        \
                        W_ID(node) = W_TREE_NEXT_LEFTMOST(W_ID(node));                         \
                        goto W_LABEL(6,Child);                                                 \
                    }                                                                          \
                    , /* else */                                                               \
                    if (W_ID(node) && W_TREE_NEXT_RIGHTMOST(W_ID(node))                        \
                        && W_DYNAMIC_STACK_PEEK_SAFE(W_ID(stack),NULL) == W_TREE_NEXT_RIGHTMOST(W_ID(node)) ) { \
                        W_DYNAMIC_STACK_POP(W_ID(stack));                                      \
                        W_DYNAMIC_STACK_PUSH( W_ID(stack),W_ID(node) );                        \
                        W_ID(node) = W_TREE_NEXT_RIGHTMOST(W_ID(node));                        \
                        goto W_LABEL(6,Child);                                                 \
                    }                                                                          \
                )                                                                              \
                Child = W_ID(node);                                                            \
                W_ID(node) = NULL;                                                             \
            )                                                                                  \
            W_AFTER(W_CAT(Child,po8),                                                          \
                W_ID(_finish_) = W_DYNAMIC_STACK_IS_EMPTY(W_ID(stack));                        \
                if (W_ID(_finish_))                                                            \
                    W_DYNAMIC_STACK_FREE(W_ID(stack));                                         \
            )                                                                                  \
            /**/

可以like this使用它。要反转后置顺序,请将W_REVERSED重新定义为1。要更改下一个字段获取操作,请重新定义W_TREE_NEXT(tree,ix),对于可变度数树,请重新定义W_TREE_GET_DEGREE(tree)

#include <wondermacros/tree/for_each.h>

struct bintree {
    struct bintree* next[2];
    const char* value;
};
struct bintree* root = ...

W_TREE_FOR_EACH_POSTORDER(struct bintree, node, root) {
        printf("%s\n", node->value);
}