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

C++で先行順走査(プレオーダー)から二分探索木(BST)の後行順走査(ポストオーダー)を求める方法

この問題では、二分探索木(BST:Binary Search Tree)の先行順走査(プレオーダートラバーサル)を表す配列 preOrder[] が与えられます。求めるのは、この先行順走査の情報だけをもとに後行順走査(ポストオーダートラバーサル)を導き出すことです。

入出力例で問題を理解しよう

入力

preOrder[] = {5, 2, 4, 7, 12}

出力

{4, 2, 12, 7, 5}

解決アプローチ

最もシンプルな解法は、与えられた先行順走査から実際にBSTを構築し、その木に対して後行順走査を行うことです。確かにこの方法でも正しい答えは得られますが、木を明示的に構築しなくても済む、より効率的なアプローチがあります。

その鍵となるのが、値の範囲(下限・上限)を設定しながら先行順配列を走査するという手法です。これにより、どの要素が左部分木に属し、どの要素が右部分木に属するかを切り分けられます。

まず、それぞれの走査の順序を確認しておきましょう。

先行順(preOrder) : 根 -> 左 -> 右
後行順(postOrder): 左 -> 右 -> 根

先行順の最初の要素は必ず根(ルート)です。この根に対する許容範囲は {INT_MIN, Root} となります。その後、先頭から順に配列を読み進めながら、現在の範囲に収まる要素を再帰的に処理していきます。まず左部分木の範囲で再帰呼び出しを行い、次に右部分木の範囲で同様に処理します。そして各ノードについては、左右の子の処理が完了した後に値を出力すれば、それがまさに後行順の出力順序になります。

解法1:再帰を用いた効率的な実装

C++プログラム例

#include <iostream>
using namespace std;
void findPostOrderTraversalRec(int pre[], int n, int lowerLimit, int
upperLimit, int& index){
    if (index == n)
       return;
    if (pre[index] < lowerLimit || pre[index] > upperLimit)
       return;
    int currNode = pre[index];
    index++;
    findPostOrderTraversalRec(pre, n, lowerLimit, currNode, index);
    findPostOrderTraversalRec(pre, n, currNode, upperLimit, index);
   cout<<currNode<<" ";
}
void findPostOrderTraversalFromPreOrder(int pre[], int n){
    int index = 0;
    findPostOrderTraversalRec(pre, n, -1000, 1000, index);
}
int main(){
    int pre[] = { 5, 2, 4, 7, 12 };
    int n = sizeof(pre) / sizeof(pre[0]);
    cout<<"PreOrder Traversal : \t";
    for(int i = 0; i < n ; i++)
       cout<<pre[i]<<" ";
    cout<<endl<<"Post Order Traversal : \t";
    findPostOrderTraversalFromPreOrder(pre, n);
    return 0;
}

実行結果

PreOrder Traversal − 5 2 4 7 12
Post Order Traversal − 4 2 12 7 5

この再帰的な手法は、BSTを実際に構築することなく O(n) の時間計算量で後行順走査を求められるため、非常に効率的です。

解法2:反復(イテレーティブ)を用いた実装

もうひとつの方法として、ループだけで完結する反復型の解法もあります。先行順が「根 → 左 → 右」、後行順が「左 → 右 → 根」であることを利用し、ピボット要素(左部分木の末尾にあたる位置)をあらかじめ計算しておきます。

具体的には、先頭の根の値より大きい値を持つ最初の要素のインデックスを探すことでピボットを特定できます。こうして得られたピボットを基準に、「左部分木の要素 → 右部分木の要素 → 根」の順に出力すれば、それがそのまま後行順走査になります。

C++プログラム例

#include <iostream>
using namespace std;
void findPostOrderTraversalFromPreOrder(int pre[], int n){
    int pivot = 0;
    for(int i = 1; i < n; i++){
       if (pre[0] <= pre[i]) {
          pivot = i;
          break;
       }
    }
    for(int i = pivot - 1; i > 0; i--){
       cout << pre[i] << " ";
    }
    for(int i = n - 1; i >= pivot; i--) {
       cout << pre[i] << " ";
    }
    cout << pre[0];
}
int main(){
    int pre[] = { 5, 2, 4, 7, 12 };
    int n = sizeof(pre) / sizeof(pre[0]);
    cout<<"PreOrder Traversal : \t";
    for(int i = 0; i < n ; i++)
       cout<<pre[i]<<" ";
    cout<<endl<<"Post Order Traversal : \t";
    findPostOrderTraversalFromPreOrder(pre, n);
    return 0;
}

実行結果

PreOrder Traversal − 5 2 4 7 12
Post Order Traversal − 4 2 12 7 5

まとめ

BSTの先行順走査から後行順走査を求めるには、①値の範囲を使った再帰的アプローチ、②ピボットを利用した反復的アプローチの2つの方法があります。どちらも木を明示的に構築せずに結果を得られる点がポイントです。特に再帰版は範囲チェックによって部分木の境界を自然に判定できるため、任意のBST形状にも柔軟に対応できます。

  1. C++で中順・後順走査から前順走査を求める方法

    問題の概要この問題では、ある二分木の中順走査(通りがけ順)と後順走査(帰りがけ順)の結果が与えられます。目的は、これらの情報をもとに木を実際に構築することなく、前順走査(行きがけ順)の結果を求めて出力することです。問題例入力: 中順走査: 16 7 21 12 1 5 9 後順走査: 16 21 7 1 9 5 12 出力: 前順走査: 12 7 16 21 5 1 9この入力から復元される二分木は次のような構造になります。 12 / \ 7 5 / \ / \ 16 21 1 9単純なアプローチ

  2. C++でレベル順走査の結果から二分探索木(BST)を構築する方法

    レベル順走査(レベル順序トラバーサル)の結果が与えられたとします。この走査結果をもとに、二分探索木(BST:Binary Search Tree)を構築する必要があります。例えば、走査結果が [7, 4, 12, 3, 6, 8, 1, 5, 10] の場合、構築される木は以下の図のようになります。この問題を解くには、再帰的なアプローチを使用します。レベル順走査の性質上、最初の要素がルートとなり、その後の要素はBSTの条件(左の子は親以下、右の子は親より大きい)に従って順番に挿入されていきます。具体的には、以下の手順で構築を進めます。まず、配列の最初の要素を取り出し、これを木のルートとします。