C 树的根节点处的迭代后序遍历中断

C 树的根节点处的迭代后序遍历中断,c,algorithm,data-structures,binary-tree,postorder,C,Algorithm,Data Structures,Binary Tree,Postorder,我实现了一个迭代打印二叉树的后序遍历的算法。整个算法都能工作,只是当它到达树的根时,它会进入无限循环 谁能给我指出正确的方向吗?这个问题我已经解决了两天了 void postorder_nonrec_2(treenode *root) { stack_t *s; stack_init(&s, 100); treenode *temp = root; while(1) { while(root) {

我实现了一个迭代打印二叉树的后序遍历的算法。整个算法都能工作,只是当它到达树的根时,它会进入无限循环

谁能给我指出正确的方向吗?这个问题我已经解决了两天了

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;
        }

    }
}

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

我认为问题在于,当右边存在子对象时,不能正确地继续弹出堆栈

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

在第一个循环中,我们将1和0推到堆栈上。然后0没有剩余子级,因此root变为null。堆栈不是空的,所以我们继续。0位于堆栈的顶部,没有正确的子级,因此我们输入if语句的第一个分支。然后我们打印出0,因为0是1的左子级--1现在是堆栈的顶部--根变为2

在这一点上,我们回到顶部。2是根并被推到堆栈上。2没有左子级,因此根变为null。堆栈不是空的。2位于堆栈的顶部。它有一个正确的子级,因此我们进入if语句的第二个分支。这使3成为根

我们回到外环的顶部。3是根并被推到堆栈上。3没有左子级,因此根变为null。堆栈不是空的。3没有正确的子级,因此我们输入if语句的第一个分支。我们打印出3张。然后因为3是2的正确子级--2现在在堆栈的顶部--我们从堆栈中弹出2,打印出2,根变为null

我们回到循环的顶部。根已为null,因此不会将任何内容推送到堆栈上。堆栈不是空的。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
        }
    }

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

我认为问题在于,当右边存在子对象时,不能正确地继续弹出堆栈

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

在第一个循环中,我们将1和0推到堆栈上。然后0没有剩余子级,因此root变为null。堆栈不是空的,所以我们继续。0位于堆栈的顶部,没有正确的子级,因此我们输入if语句的第一个分支。然后我们打印出0,因为0是1的左子级--1现在是堆栈的顶部--根变为2

在这一点上,我们回到顶部。2是根并被推到堆栈上。2没有左子级,因此根变为null。堆栈不是空的。2位于堆栈的顶部。它有一个正确的子级,因此我们进入if语句的第二个分支。这使3成为根

我们回到外环的顶部。3是根并被推到堆栈上。3没有左子级,因此根变为null。堆栈不是空的。3没有正确的子级,因此我们输入if语句的第一个分支。我们打印出3张。然后因为3是2的正确子级--2现在在堆栈的顶部--我们从堆栈中弹出2,打印出2,根变为null

我们回到循环的顶部。根已为null,因此不会将任何内容推送到堆栈上。堆栈不是空的。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
        }
    }

发布此答案以提供@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;
        }
    }
}

发布此答案以提供@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;
        }
    }
}

如果您唯一的终止子句是“is_empty”,那么请显示“is_empty”的作用,否则我们无法判断……否则,该构造将无限循环。此外,我们还需要验证树的格式是否正确。格式错误的树可能会导致正确的算法永久循环。
stack\u isEmpty(stack*s)
如果堆栈为空,则返回true。这一点似乎很明显,但是,如果堆栈是问题所在呢