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

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

配列に格納された要素のリストが与えられたとき、その要素列が二分探索木(BST)の先行順トラバーサル(プレオーダー走査)として成立するかどうかを判定する問題について解説します。

例えば、数列が {40, 30, 35, 80, 100} の場合、対応する二分探索木は次のようになります。

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

スタックを使った効率的な解法

この問題は、スタックを1つ使うことで線形時間 O(n) で解くことができます。基本的な考え方は、「先行順走査では親ノードが子ノードより先に現れる」という性質を利用し、スタックで祖先ノードの候補を管理するというものです。

具体的には、以下の手順に従います。

  • 空のスタックを定義する
  • 変数 root を負の無限大(INT_MIN)で初期化する
  • 先行順の各要素に対して、次の処理を行う:
    • 現在の要素が root より小さい場合は、BST の条件に反するため false を返す
    • 現在の要素がスタックのトップより大きい間、スタックから要素を取り除き続け、最後に取り除いた要素を新しい root とする(これは右側の部分木へ移動したことを意味する)
    • 現在の要素をスタックにプッシュする

すべての要素を処理して問題がなければ、その配列は有効な BST の先行順トラバーサルであると言えます。

サンプルコード

#include <iostream>
#include <stack>
#include <climits>
using namespace std;

bool isValidPreorder(int pre[], int n) {
    stack<int> stk;
    int root = INT_MIN;
    for (int i = 0; i < n; i++) {
        // 現在の要素が直近の親より小さい場合は不正
        if (pre[i] < root)
            return false;
        // スタックトップより大きい間、親を更新
        while (!stk.empty() && stk.top() < pre[i]) {
            root = stk.top();
            stk.pop();
        }
        stk.push(pre[i]);
    }
    return true;
}

int main() {
    int pre[] = {40, 30, 35, 80, 100};
    int n = sizeof(pre) / sizeof(pre[0]);
    if (isValidPreorder(pre, n))
        cout << "この配列はBSTの先行順トラバーサルとして有効です";
    else
        cout << "この配列はBSTの先行順トラバーサルとして無効です";
}

実行結果

この配列はBSTの先行順トラバーサルとして有効です

計算量

  • 時間計算量: O(n) — 各要素は最大でも1回プッシュされ、1回ポップされるためです。
  • 空間計算量: O(n) — 最悪ケース(昇順に並んだ配列など)では、すべての要素がスタックに積まれる可能性があります。

このように、スタックを活用することで再帰を使わずに、配列が二分探索木の先行順トラバーサルとして妥当かどうかを簡潔かつ高速に判定できます。

  1. 二分木の先行順(プレオーダー)走査を再帰的に実行するC++プログラム

    二分木の先行順走査とは木の走査(トラバーサル)はグラフ走査の一種であり、木に含まれるすべてのノードをそれぞれ一度だけ訪れて処理を行うことを指します。二分探索木における先行順走査(プレオーダー走査)では、「根 → 左部分木 → 右部分木」の順序で各ノードを訪問するのが特徴です。次のような二分木を例に考えてみましょう。この二分木に対する先行順走査の結果は 6 4 1 5 8 となります。ここからは、この先行順走査を再帰的に実行するC++プログラムを紹介します。C++による実装例#include<iostream> using namespace std; struct node {

  2. Pythonでプレオーダートラバーサルから二分探索木(BST)を構築する方法

    与えられた先行順走査(プレオーダートラバーサル)に一致する二分探索木を作成することを考えます。例えば、先行順走査が [8,5,1,7,10,12] の場合、出力は [8,5,10,1,7,null,12] となり、構築される木は以下のようになります。アルゴリズムの考え方先行順走査では、最初の要素が必ず根(ルート)になります。また、二分探索木の性質上、あるノードより小さい値は左部分木へ、大きい値は右部分木へ配置されます。この性質を利用し、スタックを使って祖先ノードを管理しながら木を組み立てていくのがポイントです。手順root := 先行順リストの0番目の要素をノードとして作成stack := 空