web-dev-qa-db-ja.com

プレオーダーリストとポストオーダーリストからツリーを再構築する

ノードのリストが2つあり、そのうちの1つはあるツリーの前順走査の表現であり、もう1つは同じツリーの後順走査の表現であるという状況を考えてみます。

これら2つのリストからツリーを正確に再構築することは可能だと思います。それを行うアルゴリズムはあると思いますが、それを証明していません。これはマスタープロジェクトの一部になるので、それが可能で正しいことを絶対的に確信する必要があります(数学的に証明されています)。しかし、それはプロジェクトの焦点では​​ないので、証明のために引用できる出典(つまり、紙または本)があるかどうか疑問に思いました。 (たぶんTAOCPにいるか?

要するに、私は、その前後のトラバーサルからツリーを再構築する、割り当て可能なリソースで実績のあるアルゴリズムが必要です。


注:問題のツリーは、おそらくバイナリではないか、バランスが取れていません。

注2:事前注文または事後注文リストのみを使用することはさらに良いでしょうが、それは可能ではないと思います。

注3:ノードは任意の数の子を持つことができます。

注4:私は兄弟の順序のみを気にします。子供が1人だけの場合、左右どちらでもかまいません。

25
NomeN

ツリーの深さを把握できないため、リストを1つだけ使用することはできません。したがって、必ず2つ以上のリストが必要です。

これが私の解決策の試みです:

データの順序を知る手段として、プレオーダートラバーサルを使用します。これは、最初のノードが最上位であり、トラバーサルの左側にあるデータがツリーの左側に属していることなどがわかっているため、理にかなっています。

ポストオーダートラバーサルによって、ツリーの深さを決定できます。たとえば、次のような構造があるとします。

      1
  2   5   6
 3 4  7

Where 2 is the parent of 3 and 4, and 5 is the parent of 7.

Preorder: 1 2 3 4 5 7 6
Postorder: 3 4 2 7 5 6 1

1で始まることがわかっています。これは、前順走査の最初のノードだからです。次に、次の番号2を確認します。ポストの順序では、番号2がノード1の前に来るため、2は1の子である必要があることがわかります。次に、3を確認します。3は2の前に来るため、3は2の子です。4は2の前で3の後です。したがって、4は2の子であり、3の子ではないことがわかります。

現在、これはノードが一意でない場合は機能しない可能性がありますが、少なくともソリューションの開始です。

編集:子の順序はこのソリューションで保持されます。これは、単純に、事前順序トラバーサルを介してノードの順序を知ってから、構造を知っているためです。ポストオーダートラバーサル経由。

Edit2:証明はここにあります: http://ieeexplore.ieee.org/Xplore/login.jsp?url=http% 3A%2F%2Fieeexplore.ieee.org%2Fiel2%2F215%2F626%2F00017225.pdf%3Farnumber%3D17225&authDecision = -2

ドキュメントを購入する必要があると思いますが...

以下は、解決策として提示された証明です。

http://www14.informatik.tu-muenchen.de/lehre/2007WS/fa-cse/tutorials/tutorial09-solutions.pdf

9
AlbertoPL

プレオーダーとポストオーダーはツリーを一意に定義しません。

一般に、単一のツリートラバーサルは、ツリーの構造を一意に定義しません。たとえば、これまで見てきたように、次の両方のツリーでは、順序トラバーサルによって[1,2,3,4,5,6]が生成されます。

    4                     3
   / \                   / \
  2   5                 2   5
 / \   \               /   / \
1   3   6             1   4   6

プレオーダーとポストオーダーのトラバーサルにも同じあいまいさが存在します。上記の最初のツリーのプレオーダートラバーサルは[4,2,1,3,5,6]です。これは、同じプレオーダートラバーサルを持つ別のツリーです。

    4
   / \
  2   1
     / \
    3   6
     \
      5

同様に、ポストオーダートラバーサル[1,3,2,6,5,4]が上記の最初のツリーと一致する別のツリーを簡単に構築できます。

33
Bill the Lizard

任意のツリー[〜#〜] t [〜#〜]を4つ組(A、B、[〜#〜] c [〜#〜][〜#〜] d [〜#〜])、Aはルートノード、Bは最初の子のルートノード、[〜#〜] c [〜#〜]はBの空でない子のベクトルであり、[〜#〜] d [〜#〜]はBの空でない兄弟。[〜#〜] c [〜#〜]および[〜#〜] d [〜#〜]の要素はそれ自体です木。

A、B、[〜#〜] c [〜#〜]および[〜#〜] d [〜#〜]のいずれも空にすることができます。 Bが空の場合、[〜#〜] c [〜#〜]および[〜#〜] d [〜#〜]でなければなりません。 Aの場合は、すべて。

ノードは一意であるため、[〜#〜] c [〜#〜]および[〜#〜] d [〜#〜]内の任意の場所に含まれるノードのセットは、ばらばらで、どちらもAまたはBを含まない。

関数pre()およびpost()は、フォームの順序付けられたシーケンスを生成します:

pre(T)= [A、B、pre([〜#〜] c [ 〜#〜]pre([〜#〜] d [〜#〜]]

post(T)= [post([〜#〜] c [〜#〜]、B、post([〜#〜] d [〜#〜]、A]

ここで、ベクトルに適用される関数は、関数を各要素に順番に適用した結果として得られるシーケンスの連結と定義されます。

次に、ケースを検討します。

  • aが空の場合、両方の関数の出力は空のシーケンスです[]
  • bが空の場合、両方の関数の出力は[A]になります。
  • [〜#〜] c [〜#〜]および[〜#〜] d [〜#〜]が空の場合、 pre(T)= [A、B]およびpost(T)= [B、A]
  • [〜#〜] c [〜#〜]が空の場合、pre(T)= [A、B 、D ']およびpost(T)= [B、D' '、A ](素数は[〜#〜] d [〜#〜]に含まれるノードの順列を示します)
  • [〜#〜] d [〜#〜]が空の場合、pre(T)= [A、B 、C ']およびpost(T)= [C' '、B、A ]
  • 空でない場合、pre(T)= [A、B、C 'D']およびpost(T)= [C ''、B、D '' =、A]

すべての場合において、AとB(存在する場合)を区切り文字として使用することにより、2つの出力シーケンスのメンバーを適切なサブシーケンスに明確に分割できます。

次に問題は、ベクトルシーケンスを分割できるかどうかです。できれば、それぞれを再帰的に処理して完了です。

pre()の結果は常にシーケンスのチェーンになりますstartingAノードで、post()の結果は常にシーケンスのチェーンになりますendingAノードを使用すると、実際にそれらを分割できます。ただし、Aノードが空になることはありません

これは、独立して空である可能性がある固定された子を持つバイナリ(または実際に任意の)ツリーの場合、プロセスが落ちる場所です。ただし、この例では[〜#〜] c [〜#〜][〜#〜] d [〜#〜]を定義して、 -ノードが空なので、再構築が機能することが保証されます。

ええと、とにかくそう思います。明らかにこれは単なる議論であり、正式な証明ではありません!

4
walkytalky

ノードに一意の名前が付けられていると仮定すると、ツリーを再構築するには、事前注文と事後のトラバーサルで十分です。そのためのアルゴリズムを作成する鍵は、それを理解することです

Xは、Xが先行注文でYの前にあり、ポストオーダーでYの後にある場合に限り、Yの祖先です。

これにより、ノードのすべての子孫を常に見つけることができます。 Xの子孫は、常にpreorderのXの直後にあり、postorderのXの前にあります。したがって、Xをルートとするサブツリーの作成に関心があることがわかったら、Xをルートとするサブツリーの前順トラバースと後順トラバーサルを抽出できます。これにより、Xの直後のノードがそれが子孫である場合、その左端の子。

スタックベースの実装もあり、これは事前注文ノードを反復処理し、次の事前注文ノードの直接の親になる候補であるノードをスタックに保持します。各先行ノードについて、次の先行ノードの親ではないすべてのノードをスタックから繰り返しポップします。そのノードをスタックの最上位ノードの子にし、子をスタックにプッシュします。

1
user2864574

他の人がすでに指摘しているように、二分木は前後のトラバーサルのみを使用して再構築することはできません。単一の子ノードには、それが左子か右子かを識別できないあいまいなトラバーサルがあります。プレオーダーおよびポストオーダートラバーサルを以下のように検討してください。プレオーダー:a、bポストオーダーb、a

次の両方のツリーを生成できます

a a \/b b順序トラバーサルなどの追加情報がないと、bがaの左または右の子であるかどうかを知ることは不可能です。

1
shyamala

この制限を持つバイナリツリーを作成します。このノードには少なくとも1つのノードがあり、このノードには子が1つだけあります(右または左、違いはありません)。

次に、プレオーダーリストとポストオーダーリストを記述します。次に、これらのリストからツリーを再構築してみてください。そのノードでは、その子が右か左かを決定できないことに気付きます。

1
Maryam A

事前注文と事後注文のトラバーサルから一般的なバイナリツリーを構築することはできません(これを参照してください)。しかし、バイナリツリーがフルであることを知っていれば、曖昧さなくツリーを構築できます。次の例の助けを借りてこれを理解しましょう。

与えられた2つの配列をpre [] = {1、2、4、8、9、5、3、6、7}およびpost [] = {8、9、4、5、2、6、7と見なします。 、3、1}; pre []では、左端の要素はツリーのルートです。ツリーがいっぱいで、配列サイズが1より大きいため、pre []の1の隣の値は、ルートの子のままにする必要があります。つまり、1がルートで、2が子のままであることがわかります。左側のサブツリーですべてのノードを見つける方法は? 2は左側のサブツリーのすべてのノードのルートです。 post []の2より前のすべてのノードは、左側のサブツリーにある必要があります。これで、1がルート、要素{8、9、4、5、2}が左側のサブツリーにあり、要素{6、7、3}が右側のサブツリーにあることがわかりました。

              1
            /   \
           /      \
 {8, 9, 4, 5, 2}     {6, 7, 3}

上記のアプローチを再帰的に実行し、次のツリーを取得します。

      1
    /   \
  2       3
/  \     /  \

4 5 6 7/\
8 9

0
user4772217