インタビュアー: それほど単純な二分木アルゴリズムさえないのですか?

アルゴリズムに関する記事を公開するのは久しぶりです。今日は、面接でよく聞かれる質問でもある、興味深いアルゴリズムの質問を取り上げます。

この質問は leetcode の質問 572 です。要件は次のとおりです: 2 つのバイナリ ツリー A と B が与えられた場合、B が A のサブツリーであるかどうかを判断します。

以下の例では、B が A のサブツリーであることがわかります。

この問題を解決する方法を考えてみませんか?

B が A のサブツリーである場合、B は A のサブツリーとまったく同じでなければならないため、2 つのバイナリ ツリーがまったく同じかどうかを判断する関数 isSame を実装できます。この関数は実装が非常に簡単です。

 
 

bool isSame(TreeNode* a, TreeNode* b) { if (a == nullptr && b == nullptr) return true; else if (a == nullptr || b == nullptr) return false; else return a->val == b->val && isSame(a->left, b->left) && isSame(a->right, b->right);}

これを実行するには 3 行のコードしか必要とせず、関数は非常に単純です。

  1. バイナリ ツリー a と b が両方とも空の場合は、明らかに true を返します。

  2. それ以外の場合、a が空、または b が空の場合、2 つのツリーは明らかに同じではないため、false を返します。

  3. 条件 1 と 2 が満たされない場合、a と b のルート ノードの値が同じで、左右のサブツリーが同じであれば、二分木 a と b は同じ二分木であることを返します。真実

isSame 関数を使用すると、残りは簡単です。バイナリ ツリー a を走査して b が a の同じサブツリーであるかどうかを判断するときに isSame 関数を継続的に呼び出すだけで済みます。

同様に、3 行のコードのみで実行できます。

 
 

bool isSubtree(TreeNode* root, TreeNode* subRoot) { if (root == nullptr && subRoot == nullptr) return true; else if (root == nullptr || subRoot == nullptr) return false; else return isSame(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);}

コードは非常に単純で、バイナリ ツリーの通常の走査です。

学生の中には、このアルゴリズムが実際にはあまり効率的ではないことに気づいた人もいるかもしれません。その理由は、バイナリ ツリー a の各ノードに対して isSame 関数を呼び出す必要があるからです。バイナリ ツリー a のノードの数が M である場合、その数は二分木 b 内のノードの数は N である場合、アルゴリズムの時間計算量は O(M*N) です。

バイナリツリーの各ノードに対して isSame 関数を呼び出す必要がありますか? 実際にはこれは必要ありません。

より効率的なアルゴリズムを考え出すには、エンコーディングの概念を理解する必要があります。

md5 に精通している学生は、任意のファイルの md5 値を計算できることを知っています。md5 は、指紋と同じように数字の文字列です。まったく同じであるのは 2 つのファイルだけなので、これら 2 つのファイルの md5 はまったく同じです。 md5 を比較することで、2 つのファイルがまったく同じであるかどうかを確認できます。Linux では、md5sum コマンドを使用してファイルの md5 値を計算します。

 
 

$ md5sum a.c6004b6a21b274b405a2bd1f1c75a93c7 a.c

同様に、バイナリ ツリーの「md5」値を計算することもできます。

どのように計算するのでしょうか?実はとてもシンプルなのです。

バイナリ ツリーの事前順序トラバーサル中に「トラバーサル トラック」を出力するだけで済みます。その後、バイナリ ツリーを string にシリアル化できます

二分木 b が a のサブツリーである場合、二分木 b のシリアル化された文字列は、a のシリアル化された文字列の部分文字列でなければなりません。

このようにして、エンコードを通じて、二分木の部分木の判定問題を文字列の部分文字列マッチング問題に変換し、文字列マッチング問題は古典的な KMP アルゴリズムで解決できます。

バイナリ ツリーを文字列にシリアル化する関数も簡単です。

 
 

void serialize(TreeNode* root, string& str) { if (root == nullptr) { str = str + "#"; } else { str = str + "," + to_string(root->val);
serialize(root->left, str); serialize(root->right, str); }}

ご覧のとおり、コードは実際にはバイナリ ツリーのトラバーサルです。

バイナリ ツリーを文字列にシリアル化できるようになったので、次のステップは簡単です。

 
 

bool isSubtree(TreeNode* root, TreeNode* subRoot) { string a, b; serialize(root, a); serialize(subRoot, b); return a.find(b)!=string::npos;}

バイナリ ツリーを文字列にシリアル化した後は、文字列 b が文字列 a の部分文字列であるかどうかを判断するだけで済みます。

このトピックから、情報コーディングの重要な役割がわかります。これは習得する価値のあるアイデアでもあり、時には問題を解決する効果もあります

おすすめ

転載: blog.csdn.net/2301_77463738/article/details/131152139