バイナリツリーのノードをファイルに書き込む必要があります。バイナリツリーを書く最もスペース効率の良い方法は何ですか。親をi
に配置し、その子を2i
、2i+1
に配列形式で格納できます。しかし、これはスパースな二分木の場合に多くのスペースを浪費します。
私が好む1つの方法は、プレオーダートラバーサルを格納することですが、そこに「null」ノードを含めることもできます。 「null」ノードを格納すると、ツリーの順序も格納する必要がなくなります。
この方法のいくつかの利点
たとえば、64ビット整数のバイナリツリーがあるとすると、各ノードの後に次のビットがnullノードかどうかを示す追加のビットを格納できます(最初のノードは常にルートです)。ノードがnullの場合、1ビットで表すことができます。
したがって、n個のノードがある場合、スペースの使用量は8nバイト+ n-1標識ビット+ nullノードのn + 1ビット= 66 * nビットになります。
Pre/post + inorderでは、16nバイト= 128 * nビットを使用することになります。
したがって、このpre/post + inorderメソッドよりも62 * nビットのスペースを節約できます。
木を考える
100
/ \
/ \
/ \
10 200
/ \ / \
. . 150 300
/ \ / \
. . . .
どこ '。' nullノードです。
100 10 . . 200 150 . . 300 . .
としてシリアル化します
これで、各(サブツリーを含む)「nullを使用したプレオーダートラバーサル」には、nullノードの数=ノードの数+ 1というプロパティがあります。
これにより、最初のノードがツリーのルートであるため、シリアル化されたバージョンを1回のパスで指定してツリーを作成できます。後続のノードは、左側のサブツリーの後に右側が続き、次のように表示できます。
100 (10 . .) (200 (150 . .) (300 . .))
インオーダートラバーサルを作成するには、ノードが表示されたときにスタックとプッシュを使用し、nullが表示されたときに(リストに)ポップします。結果のリストは、順序のトラバーサルです(これの詳細な説明はここにあります C++/C/Java:Anagrams-元の文字列からターゲットへ; )。
XMLについて考えてください。これは一種のツリーのシリアル化です。例えば:
<node id="1">
<node id="2"> 1
</node> / \
<node id="3"> 2 3
<node id="4"> / \
</node> 4 5
<node id="5">
</node>
</node>
</node>
では、なぜスペースとタグなのか?私たちはそれらを段階的に省略できます:
<1>
<2></>
<3>
<4></>
<5></>
</>
</>
スペースを削除します:<1><2></2><3><4></><5></></></>
。
山かっこを削除します:12/34/5///
ここで問題は、ノードに空の左サブツリーと空でない右サブツリーがある場合はどうなりますか?次に、別の特殊文字「#」を使用して、空の左サブツリーを表すことができます。
例えば:
1
/ \
2
/ \
3
このツリーは次のようにシリアル化できます:1#23///
。
(ほぼ)完全なツリーがある場合、2i、2i + 1(バイナリヒープ)メソッドは確かに最良の方法です。
それ以外の場合、ParentId(親インデックス)を各ノードに格納することを回避できません。
in-order
およびpre/post-order
ファイル内のバイナリツリーの走査を行い、これらの走査からツリーを再構築します。