2009-09-27 13 views

答えて

23

それは小さな問題だが、次のように私は、以前のソリューションを適応させるだろう...

eq(t1, t2) = 
    t1.data=t2.data && eq(t1.left, t2.left) && eq(t1.right, t2.right) 

理由はメートルということですismatchesは一般的である可能性が高いため、早期に検出してから比較をやり直すことをお勧めします。もちろん、私はショートサーキットであると仮定しています& &演算子はここにあります。

これは、構造的に異なるツリーを正しく処理し、再帰を終了することでいくつかの問題が解決されることを指摘します。基本的には、t1.leftなどのヌルチェックが必要です。一方のツリーに.leftがなくても、他方のツリーには構造的な違いがあります。両方とも空白がある場合、差はありませんが、リーフに達しました。それ以上は再帰しません。両方の.left値がNULLでない場合にのみ、サブツリーをチェックするために繰り返します。もちろん、同様のことが当てはまります。

たとえば、次のようなチェックを含めることができます。 (t1.left == t2.left)、これは、2つのツリーに対してサブツリーを物理的に共有できる(同じデータ構造ノード)場合にのみ意味があります。このチェックは、t1.leftとt2.leftが同じ物理ノードである場合、それらのサブツリー全体が同一であることを既に知っています。

ACの実装がコメントに応えて...

bool tree_compare (const node* t1, const node* t2) 
{ 
    // Same node check - also handles both NULL case 
    if (t1 == t2) return true; 

    // Gone past leaf on one side check 
    if ((t1 == NULL) || (t2 == NULL)) return false; 

    // Do data checks and recursion of tree 
    return ((t1->data == t2->data) && tree_compare (t1->left, t2->left) 
           && tree_compare (t1->right, t2->right)); 
} 

EDITかもしれない...これを使用して完全なツリーを比較するための

実行している時間が最も簡単(Oとして記載されていますn)ここで、nはちょっと木のサイズです。より複雑な境界を受け入れる場合は、O(最小(n1、n2))のような小さなものを得ることができます。ここで、n1とn2はツリーのサイズです。

説明は、基本的に、再帰呼び出しは左ツリーの各ノードに対して1回だけ実行され、右ツリーの各ノードに対して1回のみ作成されます。関数自体(再帰を除く)は最大でも一定量の作業しか指定しないので(ループはありません)、すべての再帰呼び出しを含む作業は、小さなツリー時間の定数と同じくらいです。

ツリーの交差点のアイデアを使用して、より複雑な小さい境界線をさらに解析することができますが、大きなOは、上限を示しています。これをコンポーネントとしてより大きなアルゴリズム/データ構造を構築しようとしない限り、おそらくこの分析を行う価値はありません。その結果、いくつかのプロパティが常にそれらのツリーに適用され、より大きなアルゴリズム。

タイガーバインドを形成する1つの方法は、両方のツリーのノードへのパスのセットを検討することです。各ステップは、L(左のサブツリー)またはR(右のサブツリー)です。したがって、ルートは空のパスで指定されます。ルートの左の子の右の子は "LR"です。有効なパスの集合をツリー内に表現する関数 "paths(T)"(数学的にはプログラムの一部ではない)を定義する。

だから我々は...

paths(t1) = { "", "L", "LR", "R", "RL" } 
paths(t2) = { "", "L", "LL", "R", "RR" } 

同じパス指定は両方のツリーに適用されている場合があります。そして、それぞれの再帰は、常に両方のツリーの同じ左/右のリンクに従います。したがって、再帰はこれらの集合の反復でパスを訪れ、これを使って指定できる最も厳密な境界は、その交差のカーディナリティです(再帰呼び出しごとの定数に依然としてバインドされています)。上記のツリー構造については

、我々は次のパスのために再帰を行う...

paths(t1) intersection paths(t2) = { "", "L", "R" } 

ので、この場合は私たちの仕事は、非再帰的な仕事の最大3​​倍の最大コストに制限されtree_compare関数です。

これは通常、不必要な詳細量ですが、明らかにパスセットの交点は最小の元のツリー内のノードの数と同じくらいです。また、O(n)のnが1つの元のツリーのノード数または両方のノードの合計を参照するかどうかは、明らかに最小値または最小値のいずれよりも小さくなりません。したがって、O(n)はそのような厳密な境界ではありませんが、私たちが話しているサイズがあいまいであっても、依然として有効な上限です。

+0

@スティーブ:正確な説明のためにスティーブに感謝します。 – Rachel

+0

このアルゴリズムの実行時間はO(n^2)ですか? –

+0

@Himanshu Jindal - それはO(n)です - 詳細は編集をご覧ください。 – Steve314

1

おそらく達成しようとしているものの一般的な用語はgraph isomorphismです。このページでこれを行うアルゴリズムがいくつかあります。

+1

FWIWでは、木の同型性は線形時間で確認できます。 – ShreevatsaR

4

モジュロスタックオーバーフロー、

eq(t1, t2) = 
    eq(t1.left, t2.left) && t1.data=t2.data && eq(t1.right, t2.right) 

ようなもの(これはすべてのツリー構造代数データ型の等価述語に一般化 - 構造化データのどの部分のために、そのサブ部分の各々が等しいかどうかを確認他の1のサブ部分のそれぞれに。)

+0

ブライアンに感謝の言葉をくれてありがとう。 – Rachel

0

私は以下のように書いています。あなたのデータ型は(例えばない辞書やリスト)ハッシュ可能である場合に、次のコードは、ほとんどの関数型言語では、とさえPythonで動作します。(同じ構造で、すなわちTree(1,Tree(2,3))==Tree(Tree(2,3),1)

  • トポロジカル平等:

    tree1==tree2set(tree1.children)==set(tree2.children)

  • 平等を命じた意味:

    tree1==tree2tree1.children==tree2.children

が平等が既に彼らのために定義されているので、あなたは、ベースケース(葉)を処理する必要はありません

(Tree.childrenは子供の順序付けられたリストである)を意味します。

+0

あなたは 'set(tree1.children)== set(tree2.children)'が 'Tree(0、Tree(1、Tree(2,3)))== Tree(0、Tree (1,3)、2)) '? – jfs

+0

@ JFSebastian:正しく動作する(つまり、 "等しくない"結果を返します): '0 == 0'と'(1、(2,3))==((1,3)、2 ) '?最初に '(1、(2,3))==((1,3)、2)'をチェックしましょう。 '1 ==(1,3)'と '(2,3)== 2 'ですか?いいえ、木は原子と等しくないからです。したがって、 '(1、(2,3))==((1,3)、2)'は真実ではありません。したがって、 '(0、(1、(2,3)))==(0、((1,3)、2))'は真ではありません。アイデアは、設定された等価性は、その要素の等価性について再帰的に定義されるため、等価性チェックは本質的に再帰的であるということです。 – ninjagecko

+0

なぜ '1 == 2'と'(2,3)==(1,3) 'チェックをスキップしますか? 'set()'定義は要素の特定の順序を意味しますか?それ以外の場合は非常に高価です(http://www.wolframalpha.com/input/?i=a%5Bn%5D+%3D%3D+1+%2B+2+a%5Bn+-+1%5D%2C + a%5B0%5D +%3D%3D + 1)。*トポロジカル*は、ノードの値が重要ではなく、ツリーの形状だけが重要であるということを意味しますが(例:トポロジスト、コーヒーカップ、ドーナツは同じものです](http://www.youtube.com/watch?v=4iHjt2Ovqag) – jfs

3

また、2つのトラバーサル(プリオーダー、ポストオーダー、またはインオーダー)のいずれかを実行し、両方のツリーの結果を比較することもできます。それらが同じであれば、それらの同等性を確かめることができます。

1

それが証明されたという事実ですので - 次のように長い間、我々が持っているように、バイナリツリーを再作成することが可能であるが:

  1. で、注文トラバーサルにおいて遭遇するノードのシーケンス。その後、
  2. プレオーダーまたはPOST-注文トラバーサル

2進木がインオーダーと同じとしている場合は、[プリオーダーまたはポスト順]列に遭遇しているノードのシーケンス、彼ら構造的にも価値観においても同等でなければならない。

各トラバーサルはO(n)操作です。トラバーサルは合計4回実行され、同じトラバーサルの結果が比較されます。 O(n)* 4 + 2 => O(n) したがって、時間の複雑さの合計順序はO(n)

関連する問題