C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で二分木内の重複するサブツリーをすべて検出する方法

問題の概要

二分木が与えられたとき、その中に重複するサブツリー(部分木)が存在するかどうかを判定する問題を考えてみましょう。例として、次のような二分木を取り上げます。

C++で二分木内の重複するサブツリーをすべて検出する方法

この木には、サイズ2の同一のサブツリーが2つ存在します。さらに、それぞれのサブツリー内のDに注目すると、BDとBEもまた重複するサブツリーになっています。

解決のアプローチ:木のシリアライズとハッシュ

この問題は、木のシリアライズ(直列化)ハッシュテーブルを組み合わせることで効率的に解決できます。基本的な考え方は以下のとおりです。

  • 各サブツリーを間順走査(inorder traversal)で文字列としてシリアライズする
  • 空のノードには開き括弧と閉じ括弧を挿入し、木の構造を一意に表現できるようにする
  • シリアライズ結果をハッシュテーブルに記録し、同じ文字列が2回以上現れたノードを重複サブツリーの根として検出する

括弧で構造を明示することで、ノードの構成が異なるサブツリーが同じ走査結果を持つことを防ぎ、正確な重複判定が可能になります。

C++による実装例

#include <iostream>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
using namespace std;

struct Node {
public:
    char data;
    Node *left, *right;
};

Node* getNode(char key) {
    Node* newNode = new Node;
    newNode->data = key;
    newNode->left = newNode->right = NULL;
    return newNode;
}

unordered_set<string> subtrees;

string inorder(Node* node, unordered_map<string, int>& map) {
    if (!node)
        return "";
    string str = "(";
    str += inorder(node->left, map);
    str += to_string(node->data);
    str += inorder(node->right, map);
    str += ")";
    if (map[str] == 1)
        cout << node->data << " ";
    map[str]++;
    return str;
}

void duplicateSubtreeFind(Node *root) {
    unordered_map<string, int> map;
    inorder(root, map);
}

int main() {
    Node *root = getNode('A');
    root->left = getNode('B');
    root->right = getNode('C');
    root->left->left = getNode('D');
    root->left->right = getNode('E');
    root->right->right = getNode('B');
    root->right->right->right = getNode('E');
    root->right->right->left = getNode('D');
    duplicateSubtreeFind(root);
}

実行結果

D E B

コードのポイント

inorder関数は再帰的にサブツリーを走査し、各サブツリーを「(左部分木)(ノードの値)(右部分木)」という形式の文字列に変換します。この文字列がハッシュマップに登録されてから2回目に出現したタイミング(カウントが1のとき)でノードの値を出力することで、重複するサブツリーの根だけを一度ずつ検出できます。

計算量について補足すると、サブツリーごとに文字列を連結してシリアライズするため、最悪の場合の時間計算量はO(n²)となります。ただし、ハッシュを用いることで木全体を1回の走査で処理でき、実用上は十分に高速に動作するのが大きなメリットです。

  1. C++で二分木内の重複するサブツリーをすべて検出する方法

    問題の概要二分木が与えられたとき、その中に重複するサブツリー(部分木)が存在するかどうかを判定する問題を考えてみましょう。例として、次のような二分木を取り上げます。この木には、サイズ2の同一のサブツリーが2つ存在します。さらに、それぞれのサブツリー内のDに注目すると、BDとBEもまた重複するサブツリーになっています。解決のアプローチ:木のシリアライズとハッシュこの問題は、木のシリアライズ(直列化)とハッシュテーブルを組み合わせることで効率的に解決できます。基本的な考え方は以下のとおりです。各サブツリーを間順走査(inorder traversal)で文字列としてシリアライズする空のノードには開

  2. C++で二分木における単一値の部分木を数える方法

    二分木が与えられたとき、その木に含まれる「単一値の部分木(Single Valued Subtree)」の個数を求めるのが本記事の目的です。単一値の部分木とは、その部分木を構成するすべてのノードが同じ値を持つような部分木のことを指します。問題の例例として、次のような二分木を考えてみましょう。この木には、以下に示す4つの単一値の部分木が存在します。解法のアプローチ:ボトムアップ方式この問題は、ボトムアップ(下から上へ)の再帰的なアプローチで効率的に解くことができます。基本的な考え方は次のとおりです。各ノードを訪問する際、そのノードを根とする部分木が単一値であるかどうかを判定し、単一値であればカウ