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

C++で再帰を使わずにN分木を先行順走査(プレオーダートラバーサル)する方法

はじめに

本記事では、N分木(N-ary Tree)が与えられたときに、その先行順走査(プレオーダートラバーサル)の結果を出力する問題を扱います。ポイントは、再帰呼び出しを使わずにスタックだけで実装するところです。

基本用語の確認

N分木(N-ary Tree)とは、すべてのノードが最大N個の子ノードを持つことができる木構造のことです。たとえば2分木(バイナリツリー)は、各ノードが最大2つの子ノードを持ちます。

先行順走査(Preorder Traversal)は、木のノードを巡回する方法の1つで、まずルートノードを訪問し、その後、子ノードを左から順に訪問していきます。

問題例

次のようなN分木を考えてみましょう。

C++で再帰を使わずにN分木を先行順走査(プレオーダートラバーサル)する方法

この木の先行順走査の結果は次のようになります。

12  15  1  4  25  99  4  11  7  19

アルゴリズム:スタックを使った解法

再帰を使わずに先行順走査を実現するには、スタックデータ構造を利用します。手順は以下のとおりです。

  1. ルートノードをスタックにプッシュします。
  2. スタックからノードをポップし、その値を出力します。
  3. ポップしたノードの子ノードを、右から左の順でスタックにプッシュします。こうすることで、スタックの性質(LIFO)により左側の子が先にポップされます。
  4. スタックが空になるまで、手順2〜3を繰り返します。

C++による実装例

#include <bits/stdc++.h>
using namespace std;
struct Node {
    int key;
    vector<Node*> child;
};
Node* insertNode(int key){
    Node* temp = new Node;
    temp->key = key;
    return temp;
}
void preOrderTraversal(struct Node* root){
    stack<Node*> tree;
    tree.push(root);
    while (!tree.empty()) {
        Node* curr = tree.top();
        tree.pop();
        cout<<curr->key<<"\t";
        vector<Node*>::iterator it = curr->child.end();
        while (it != curr->child.begin()) {
            it--;
            tree.push(*it);
        }
    }
}
int main(){
    Node* root = insertNode(12);
    (root->child).push_back(insertNode(15));
    (root->child).push_back(insertNode(99));
    (root->child).push_back(insertNode(4));
    (root->child).push_back(insertNode(7));
    (root->child[0]->child).push_back(insertNode(1));
    (root->child[0]->child).push_back(insertNode(4));
    (root->child[0]->child).push_back(insertNode(25));
    (root->child[2]->child).push_back(insertNode(11));
    (root->child[3]->child).push_back(insertNode(19));
    cout<<"PreOrder Traversal of the tree is :\n";
    preOrderTraversal(root);
    return 0;
}

出力結果

PreOrder Traversal of the tree is :
12  15  1  4  25  99  4  11  7  19

計算量

各ノードはスタックにちょうど1回プッシュされ、1回ポップされるため、時間計算量はO(N)(Nはノード数)です。空間計算量は、スタックに最大で木の高さ分のノードが積まれるため、最悪ケースでO(N)となります。

まとめ

再帰を使わずにN分木を先行順走査するには、子ノードをスタックに「右から左」の順で積むことが重要です。このテクニックは、木が深くなり再帰によるスタックオーバーフローのリスクがある場合や、処理を反復形式で統一したい場合に特に役立ちます。

  1. Pythonで二分木の先行順走査(プレオーダートラバーサル)を実装する方法

    Pythonでの二分木の先行順走査(プレオーダートラバーサル)とは二分木が与えられたとき、その木を先行順走査(プレオーダートラバーサル)で巡回した結果を返すことを考えます。先行順走査とは、「根のノード → 左部分木 → 右部分木」の順序でノードを訪問する木構造の基本的な走査手法です。例えば、次のような二分木があるとします。この木に対する先行順走査の結果は [3, 9, 20, 15, 7] となります。アルゴリズムの手順ここでは、再帰を使わずにスタックを利用した反復的なアプローチで問題を解きます。手順は以下の通りです。結果を格納するための空リスト res と、スタックとして使用する空リスト s

  2. C++で二分木の前順走査(プレオーダー)をスタックにより非再帰的に実装するプログラム

    木の走査(ツリートラバーサル)はグラフ走査の一種で、木に含まれるすべてのノードをそれぞれ一度だけ訪問して確認・出力する処理のことです。二分探索木における前順走査(プレオーダー走査)では、「根(Root)→ 左部分木(Left)→ 右部分木(Right)」という順序でノードを訪問します。 本記事では、再帰呼び出しを使わずスタックを活用して前順走査を非再帰的に実装するC++プログラムを、コード例とともにわかりやすく解説します。 前順走査の例 たとえば、次のような二分木が与えられたとします。 この木に対する前順走査の結果は次のとおりです。 前順走査の結果:5 3 2 4 8 9 非再帰的な前順走査