Обратный порядок после размещения бин-дерева, без рекурсии, без флага узла - PullRequest
1 голос
/ 05 августа 2010

Есть ли другой способ сделать это? Просто потратил 2 часа, пытаясь понять это. У меня есть решение (см. DumpPostOrder ниже), однако, есть лучший или более эффективный метод? Такое ощущение, что может быть. Правила - нет рекурсии, и узлы не могут иметь флаг посещений. Т.е. вы можете использовать только левый + правый член.

Мой подход состоял в том, чтобы уничтожить дерево в процессе. Установив дочерние элементы каждой стороны в null, вы можете пометить узел как пройденный один раз, но я также смотрю на каждый узел с дочерними элементами дважды :(. Есть ли лучший способ быстрее? но не обязательно (т.е. буду голосовать, но не отмечу ответ). Спасибо!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BinaryTreeNoRecursion
{
    public class TreeNode<T>
    {
        public T Value { get; set; }

        public TreeNode<T> Left { get; set; }
        public TreeNode<T> Right { get; set; }

        public TreeNode(T inValue)
        {            
            Value = inValue;
        }

        public TreeNode(TreeNode<T> left, TreeNode<T> right, T inValue)
        {
            Left = left;
            Right = right;
            Value = inValue;
        }
    }

    public class BinaryTree<T>
    {
        private TreeNode<T> root;
        public TreeNode<T> Root
        {
            get { return root; }            
        }

        public BinaryTree(TreeNode<T> inRoot)
        {
            root = inRoot;
        }

        public void DumpPreOrder(T[] testme)
        {
            Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
            stack.Push(root);
            int count =0;
            while (true)
            {
                if (stack.Count == 0) break;

                TreeNode<T> temp = stack.Pop();                

                if (!testme[count].Equals(temp.Value)) throw new Exception("fail");

                if (temp.Right != null)
                {
                    stack.Push(temp.Right);
                }

                if (temp.Left != null)
                {
                    stack.Push(temp.Left);
                }

                count++;
            }

        }

        public void DumpPostOrder(T[] testme)
        {

            Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
            TreeNode<T> node = root;
            TreeNode<T> temp;
            int count = 0;
            while(node!=null || stack.Count!=0) 
            {   
                if (node!=null)
                {
                    if (node.Left!=null)
                    {                       
                        temp = node;
                        node = node.Left;
                        temp.Left = null;
                        stack.Push(temp);                        

                    }
                    else
                    if (node.Right !=null)
                    {
                        temp = node;
                        node = node.Right;
                        temp.Right= null;
                        stack.Push(temp);
                    }           
                    else //if the children are null
                    {
                        if (!testme[count].Equals(node.Value)) throw new Exception("fail");
                        count++;
                        if (stack.Count != 0)
                        {
                            node = stack.Pop();
                        }
                        else
                        {
                            node = null;
                        }
                    }       
                }
            }

        }

        public void DumpInOrder(T[] testme)
        {

            Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();            
            TreeNode<T> temp = root;
            int count = 0;
            while (stack.Count!=0 || temp!=null)
            {                
                if (temp != null)
                {                    
                    stack.Push(temp);
                    temp = temp.Left;
                }
                else
                {
                    temp = stack.Pop();
                    if (!testme[count].Equals(temp.Value)) throw new Exception("fail");
                    count++;          
                    temp = temp.Right;
                }

            }
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            //create a simple tree
            TreeNode<int> node = new TreeNode<int>(100);
            node.Left = new  TreeNode<int>(50);
            node.Right = new  TreeNode<int>(150);
            node.Left.Left = new TreeNode<int>(25);
            node.Left.Right = new TreeNode<int>(75);
            node.Right.Left  = new TreeNode<int>(125);
            node.Right.Right = new TreeNode<int>(175);
            node.Right.Left.Left = new TreeNode<int>(110);

            int[] preOrderResult = { 100, 50, 25, 75, 150, 125, 110, 175};
            int[] inOrderResult = { 25, 50, 75, 100, 110, 125, 150, 175};
            int[] postOrderResult = { 25, 75, 50, 110, 125, 175, 150, 100 };
            BinaryTree<int> binTree = new BinaryTree<int>(node);

            //do the dumps, verify output
            binTree.DumpPreOrder(preOrderResult);
            binTree.DumpInOrder(inOrderResult);
            binTree.DumpPostOrder(postOrderResult);
        }
    }
}

Ответы [ 4 ]

1 голос
/ 05 августа 2010

Избегать рекурсии в этом случае, вероятно, плохая идея, как отмечалось ранее. Стек системных вызовов предназначен для обработки подобных вещей. Уничтожение вашего дерева - это форма разметки узлов.

Если вы хотите использовать свой собственный стек, тогда вам нужно добавить немного больше информации, чем просто узел. Помните, что стек системных вызовов содержит как программный счетчик, так и параметры функции (а также локальные переменные, которые здесь не важны). Мы можем нажать кортежи вида (PushMyChildren, node), (PrintMe, Node), и когда мы вытолкнем узел формы (PushMyChildren, node), мы нажмем (PrintMe, Node), затем (PushMyChildren, right child) и затем (PushMyChildren, left child). Если левых и правых детей не существует, не толкайте их. Когда мы извлекаем узел вида (PrintMe, Node), мы печатаем узел. В псевдо C # (я не знаю C # и у меня нет времени искать правильные типы и синтаксис).

public void DumpPostOrder(T[] testme)
{
  enum StackState {printNode, pushChildren} 
  Stack< Pair<StackState, TreeNode<T> > > stack = new Stack< Tuple<StackState, TreeNode<T> > >();
  stack.Push(new Pair(pushChildren, root);
  while ( stack.Count != 0 ) {
    Pair<StackState, TreeNode<T> > curr = stack.pop();
    if (curr.First ==  printNode) {
       // process the node in curr.Second
    } else {
       node = curr.Second;
       stack.Push(new Pair(printNode, node));
       if (node.Right != null) {
         stack.Push(new Pair(pushChildren, node.Right))
       }
       if (node.Left != null) {
         stack.Push(new Pair(pushChildren, node.Left))
       }
    }
  }
1 голос
/ 05 августа 2010

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

Редактировать

Я думаю, что это будетдопустимая опция:

1) Хранить двунаправленный связанный список указателей на узлы в дереве.
2) Начать с корневого узла.
3) Добавить корневой указатель в список.
4) Перейдите к правому потомку.
5) Добавьте указатель текущего узла в список.
6) Повторяйте шаги 4 и 5, пока не останется нужного потомка.
7) Запишите текущий узел для публикации-order-traversal.
8) Установить текущий узел последним узлом в списке.
9) Перейти к левому потомку.
10) Добавить указатель текущей заметки в список.
11) Повторить шаги 4через 10, пока список не станет пустым.

По сути, все узлы дерева имеют указатель на своего родителя.

1 голос
/ 05 августа 2010

Мне кажется, что уничтожение дерева при его обходе довольно жестоко.

В данный момент вы создаете коллекцию посещенных узлов.

Вы отмечаете узлы как посещенные, устанавливая их в нуль.

Не могли бы вы вместо этого проверить посещение, проверив наличие узла в вашей Коллекции? Для эффективности вам может не потребоваться использование стека, но это деталь реализации.

0 голосов
/ 16 мая 2016

Я только что сделал пост-заказ в Java, используя обход по ширине (используя очередь).

        private void init(){
            if (initialized) return;
            stack = new Stack<>();
            stack.push(root);
            travers(root.right);
            travers(root.left);
            initialized = true;
        }

        private void travers(Node node){
            if (node == null) return;
            Queue<Node> queue = new LinkedList<>();
            queue.add(node);
            while (!queue.isEmpty()){
                Node temp = queue.poll();
                stack.push(temp);
                if (temp.right != null) queue.add(temp.right);
                if (temp.left != null) queue.add(temp.left);
            }
        }

        public T next() {
            return stack.pop().data;
        }
...