1

これを行う別の方法はありますか?それを理解するのに2時間費やしました。私には解決策があります (以下の DumpPostOrder を参照)。しかし、より効率的な方法はありますか? あってもいい感じです。ルールは次のとおりです - 再帰なし、ノードは訪問済みフラグを持つことはできません。つまり、 left + right メンバーのみを使用できます。

私のアプローチは、その過程で木を破壊することでした。各側の子を null に設定することで、ノードを 1 回トラバースしたものとしてマークできますが、子を持つ各ノードも 2 回見ています :(. より高速な方法はありますか?必須ではありません (つまり、投票しますが、回答をマークしません)。

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

4 に答える 4

1

前述のように、この場合に再帰を回避することはおそらく悪い考えです。システム コール スタックは、このようなことを処理するように設計されています。ツリーを破棄することは、ノードをマークすることの一種です。

独自のスタックを使用する場合は、ノードだけでなく、もう少し多くの情報をプッシュする必要があります。システム コール スタックには、プログラム カウンターと関数パラメーター (ここでは重要ではないローカル変数と bu) が含まれていることに注意してください。(PushMyChildren, node)、、という形式のタプルをプッシュすることができます。そして、プッシュ(PrintMe, Node)するという形式のノードをポップすると、次に、という形になります。左右の子が存在しない場合は、それらをプッシュしないでください。フォームのノードをポップすると、ノードが出力されます。疑似 C# (私は C# を知らず、正しい型と構文を調べる時間がありません)。(PushMyChildren, node)(PrintMe, Node)(PushMyChildren, right child)(PushMyChildren, left child)(PrintMe, Node)

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))
       }
    }
  }
于 2010-08-05T12:06:03.273 に答える
1

木を横断しながら木を破壊するのはかなり残忍だと私には思えます。

現在、訪問したノードのコレクションを構築しています。

ノードをnullに設定して、ノードを訪問済みとしてマークしています。

代わりに、コレクション内のノードをチェックして訪問をチェックできませんか? 効率のために、スタックを使用しない必要があるかもしれませんが、それは実装の詳細です。

于 2010-08-05T08:51:43.643 に答える
1

バイナリ ツリーを配列にマップし (ここに示すようにヒープを配列にマップする方法と同様)、そこでポストオーダー トラバーサルを実行できます。二分木を配列に変換するアクションはおそらく再帰を利用するでしょうが、木の最初の構築方法を制御している場合 (または興味をそそるアイデアを探しているだけの場合) は、単に次のように構築することができます。配列を作成し、非再帰的なポストオーダー トラバーサル (フラグなし) 問題を矮小化します。

編集

これは実行可能なオプションになると思います:

1) ツリー内のノードへのポインタの双方向リンク リストを保持します。
2) ルート ノードから開始します。
3) ルート ポインターをリストに追加します。
4) 右の子に移動します。
5) 現在のノード ポインタをリストに追加します。
6) 適切な子が存在しなくなるまで、手順 4 と 5 を繰り返します。
7) 現在のノードを post-order-traversal に書き込みます。
8) 現在のノードをリストの最後のノードに設定します。
9) 左の子に移動します。
10) 現在のノート ポインタをリストに追加します。
11) リストが空になるまで、ステップ 4 から 10 を繰り返します。

基本的に、これにより、ツリー内のすべてのノードが親へのポインターを持つようになります。

于 2010-08-05T12:46:49.947 に答える