1

我实现了一种算法,用于迭代地打印二叉树的后序遍历。整个算法都有效,只是它在到达树根时进入无限循环。

有人可以指出我正确的方向吗?我已经被这个问题困了2天了。

void postorder_nonrec_2(treenode *root)
{
    stack_t *s;
    stack_init(&s, 100);
    treenode *temp = root;

    while(1)
    {
        while(root)
        {
            push(s, root);
            root = root -> left;
        }

        if(stack_isEmpty(s))
            break;

        if(!top(s) -> right)
        {
            root = pop(s);
            printf("%d ", root -> data);

            if(root == top(s) -> left)
            {
                root = top(s) -> right;
            }
            else if(root == top(s) -> right)
            {
                printf("%d ", pop(s) -> data);

                root = NULL;
            }
        }
        else
        {
            root = top(s) -> right;
        }

    }
}
4

2 回答 2

0

发布此答案以提供@Evan VanderZee 建议的解决方案的完整代码

void postorder_nonrec_2(treenode *root)
{
    stack_t *s;
    stack_init(&s, 100);


    while(1)
    {
        while(root)
        {
            push(s, root);
            root = root -> left;
        }

        if(stack_isEmpty(s))
            break;

        if (!top(s) -> right)
        {
            root = pop(s);
            printf("%d ", root -> data);

            while (!stack_isEmpty(s) && root == top(s) -> right)
            {
                root = pop(s);
                printf("%d ", root -> data);
            }

            root = NULL;
        }
        else
        {
            root = top(s) -> right;
        }
    }
}
于 2015-09-27T17:18:30.560 回答
0

也许对于您正在使用的测试用例,只有根有一个无限循环,但我认为无限循环也可能发生在树的其他地方,具体取决于具体的树。

我认为问题在于,当右侧存在孩子时,您没有正确地继续弹出堆栈。

考虑一个简单的例子,我们有一个根节点 1,有一个左孩子 0 和一个右孩子 2,并假设 2 有一个名为 3 的右孩子。

在第一个循环中,我们将 1 和 0 压入堆栈。那么 0 没有左孩子,所以 root 变为 null。堆栈不是空的,所以我们继续。0在栈顶,没有右孩子,所以我们进入if语句的第一个分支。然后我们打印出 0,因为 0 是 1 的左孩子——1 现在是栈顶——根变成了 2。

此时我们回到顶部。2 是根并被推入堆栈。2 没有左孩子,所以根为空。堆栈不为空。2 在堆栈的顶部。它有一个右孩子,所以我们进入 if 语句的第二个分支。这使得 3 成为根。

我们回到外循环的顶部。3 是根并被推入堆栈。3 没有左孩子,所以根为空。堆栈不为空。3 没有右孩子,所以我们进入 if 语句的第一个分支。我们打印出 3。然后因为 3 是 2 的右孩子——现在 2 在栈顶——我们将 2 从栈中弹出,打印出 2,然后根变为空。

我们回到循环的顶部。根已经为空,所以没有任何东西被压入堆栈。堆栈不为空。1 位于堆栈的顶部。此时正确的做法是从堆栈中弹出 1,因为我们已经处理了它的右孩子;然而,1 在栈顶并且确实有一个右孩子,所以我们进入 if 语句的第二个分支,2 成为根。情况与前两段完全相同,1 是堆栈上唯一的元素,2 是根,所以我们得到一个无限循环回到那里。

如果我们更改示例,使 3 也有一个名为 4 的右孩子,那么,如果我没看错,我们将永远不会打印出 2,而是会循环打印出 4 和 3。

要纠正该问题,只要您正在处理的元素是堆栈顶部的右子元素,就应该继续弹出堆栈。我还没有编译或测试过这个,但我认为编写类似的东西会起作用

    if (!top(s) -> right)
    {
        root = pop(s);
        printf("%d ", root -> data);

        while (!stack_isEmpty(s) && root == top(s) -> right)
        {
            root = pop(s);
            printf("%d ", root -> data);
        }
        if (!stack_isEmpty(s) && root == top(s) -> left)
        {
            // checking root == top(s) -> left is probably redundant,
            // since the code is structured so that root is either
            // a child of top(s) or null if the stack is not empty
            root = top(s) -> right;
        }
        else
        {
            root = NULL;
            // could actually break out of outer loop here, but
            // to be more consistent with code in the question
        }
    }
于 2015-09-26T16:06:59.420 回答