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

先行順走査(プレオーダー)の結果から二分探索木(BST)を構築する ― C++による実装


ある先行順(プレオーダー)走査の結果が与えられているとします。この走査結果をもとに、元の二分探索木(BST)を復元することを考えましょう。例えば、走査結果が [10, 5, 1, 7, 40, 50] である場合、構築される木は次の図のようになります。

先行順走査(プレオーダー)の結果から二分探索木(BST)を構築する ― C++による実装

解法のアプローチ:範囲制約を利用する

この問題を効率的に解く鍵となるのは、各ノードに対して「取り得る値の範囲 {min … max}」を設定することです。具体的な手順は以下の通りです。

  • まず、範囲を {INT_MIN … INT_MAX} として初期化します。
  • 最初の要素は必ずこの範囲内に収まるため、その値でルートノードを作成します。
  • 左部分木を構築するときは、範囲を {INT_MIN … ルートの値} に更新します。この範囲に収まる値はすべて左部分木に属します。
  • 右部分木を構築するときは、範囲を {ルートの値 … INT_MAX} に更新します。

この手法では、配列の各要素を一度だけ処理すればよいため、全体の計算量は O(n) となり、単純に先頭から挿入を繰り返す方法(最悪 O(n²))よりも大幅に効率的です。

C++での実装例

#include <iostream>
using namespace std;
class node {
    public:
        int data;
        node *left;
        node *right;
};
node* getNode (int data) {
    node* temp = new node();
    temp->data = data;
    temp->left = temp->right = NULL;
    return temp;
}
node* makeTreeUtil( int pre[], int* preord_index, int key, int min, int max, int size ) {
    if( *preord_index >= size )
    return NULL;
    node* root = NULL;
    if( key > min && key < max ){
        root = getNode( key );
        *preord_index += 1;
        if (*preord_index < size){
            root->left = makeTreeUtil( pre, preord_index, pre[*preord_index], min, key, size );
            root->right = makeTreeUtil( pre, preord_index, pre[*preord_index],key, max, size );
        }
    }
    return root;
}
node *makeTree (int pre[], int size) {
    int preord_index = 0;
    return makeTreeUtil( pre, &preord_index, pre[0], INT_MIN, INT_MAX, size );
}
void inord (node* node) {
    if (node == NULL)
        return;
    inord(node->left);
    cout << node->data << " ";
    inord(node->right);
}
int main () {
    int pre[] = {10, 5, 1, 7, 40, 50};
    int size = sizeof( pre ) / sizeof( pre[0] );
    node *root = makeTree(pre, size);
    cout << "Inorder traversal: ";
    inord(root);
}

出力

Inorder traversal: 1 5 7 10 40 50

出力を見ると、構築した木に対して中間順(インオーダー)走査を行うと、値が昇順に並んで表示されています。これは木が正しく二分探索木として復元できたことを示しています。中間順走査が常にソート済みの順序を返すことは、BSTの重要な性質の一つであり、構築結果の検証手段としても活用できます。

  1. 【C++】配列が二分探索木(BST)の先行順トラバーサルとして有効かどうかを判定する方法

    配列に格納された要素のリストが与えられたとき、その要素列が二分探索木(BST)の先行順トラバーサル(プレオーダー走査)として成立するかどうかを判定する問題について解説します。例えば、数列が {40, 30, 35, 80, 100} の場合、対応する二分探索木は次のようになります。スタックを使った効率的な解法この問題は、スタックを1つ使うことで線形時間 O(n) で解くことができます。基本的な考え方は、「先行順走査では親ノードが子ノードより先に現れる」という性質を利用し、スタックで祖先ノードの候補を管理するというものです。具体的には、以下の手順に従います。空のスタックを定義する変数 root

  2. Pythonのスタックを使って後順走査(ポストオーダー)から二分探索木(BST)を構築する方法

    問題概要 二分探索木(BST)の後順走査(ポストオーダートラバーサル)の結果が1つ与えられたとき、その走査結果から元となる二分探索木を復元する問題を考えます。 例えば、入力が [6, 12, 10, 55, 45, 15] の場合、出力される木構造は次のようになります。 解法のアプローチ この問題を解くために、以下の手順に従います。 関数 solve() を定義します。引数として後順走査のリスト postorder を受け取ります。 n := postorder の要素数とします。 root := 後順走査の最後の要素を値として持つ新しいツリーノードを作成します。 stk := 空のスタッ