Итеративный обход в обратном порядке прерывается в корневом узле дерева

Я реализовал алгоритм для итеративной печати обратного обхода бинарного дерева. Весь алгоритм работает, за исключением того, что он входит в бесконечный цикл, когда достигает корня дерева.

Может ли кто-нибудь указать мне правильное направление? Я застрял на этой проблеме в течение 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;
        }

    }
}

person aditya_medhe    schedule 24.09.2015    source источник
comment
ЕСЛИ ваше единственное предложение завершения is_empty, то покажите, что делает is_empty, иначе мы не можем сказать... в противном случае конструкция бесконечно зацикливается. Далее нам нужно будет убедиться, что дерево сформировано правильно. Деформированное дерево может привести к вечному циклу правильных алгоритмов.   -  person JVene    schedule 24.09.2015
comment
stack_isEmpty(stack *s) возвращает true, если стек пуст.   -  person aditya_medhe    schedule 24.09.2015
comment
Это кажется очевидным, но что, если проблема заключается в стеке? Мы не могли видеть это отсюда. Из того, что я вижу, цикл будет продолжаться вечно, если он не сработает правильно. Иными словами, использовали ли вы отладчик, чтобы увидеть, где программа зацикливается? Разве это не вызов stack_isEmpty? Если нет, то он, вероятно, пойман в верхнем цикле в поисках крайнего левого узла, что намекает на то, что проблема может быть в самом дереве, но тогда почему не происходит переполнение памяти (создается огромный стек). Если вызывается stack_isEmpty, то почему он никогда не возвращает true? Это единственные возможности, которые я вижу   -  person JVene    schedule 24.09.2015
comment
stack_isEmpty() был протестирован и отлажен. У него нет проблем. Все, что мне нужно, это способ выяснить, что текущий элемент является корневым узлом, и все дерево было пройдено. Затем я могу извлечь корневой узел из стека, распечатать его, и он автоматически прекратит цикл.   -  person aditya_medhe    schedule 24.09.2015
comment
Это функция: int stack_isEmpty(stack_t *stack) { if(stack -> top == -1) return 1; return 0; }   -  person aditya_medhe    schedule 24.09.2015
comment
if ( temp == root ), значит, вы нашли корень дерева. На самом деле лучше инвертировать. Скажите treenode * tptr = root; и используйте tptr в алгоритме, чтобы не запутаться.   -  person JVene    schedule 24.09.2015
comment
Я поработаю над этим и вернусь к вам. Ценим ваш быстрый ответ!   -  person aditya_medhe    schedule 24.09.2015


Ответы (2)


Возможно, с используемыми вами тестовыми примерами бесконечный цикл существует только в корне, но я думаю, что бесконечный цикл может возникать и в других местах дерева, в зависимости от конкретного дерева.

Проблема, я думаю, в том, что вы неправильно продолжаете всплывать в стеке, когда дети существуют справа.

Рассмотрим простой пример, где у нас есть корневой узел 1 с левым дочерним элементом 0 и правым дочерним элементом 2, и предположим, что у 2 есть правый дочерний узел с именем 3.

В первом цикле мы помещаем 1 и 0 в стек. Тогда у 0 нет левого потомка, поэтому root становится нулевым. Стек не пуст, поэтому продолжаем. 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
        }
    }
person Evan VanderZee    schedule 26.09.2015
comment
Большие усилия и точное решение. Я заставил ваш код работать, это было как шарм. Затем я отрезал все лишнее, как вы предложили, чтобы код был коротким, и он работает! Спасибо! - person aditya_medhe; 27.09.2015

Публикация этого ответа, чтобы предоставить полный код решения, предложенного @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;
        }
    }
}
person aditya_medhe    schedule 27.09.2015