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

C++で再帰を使わずに二分木のルートからリーフへの経路を出力するプログラム

このチュートリアルでは、与えられた二分木において、ルートノードからすべてのリーフノード(葉ノード)への経路を出力するプログラムを、C++で再帰を使わずに実装する方法を解説します。

例として、次のような二分木を考えてみましょう。

C++で再帰を使わずに二分木のルートからリーフへの経路を出力するプログラム

この二分木には、34・55・29という3つのリーフノードが存在します。したがって、ルートノードからリーフノードへの経路は3つあることになります。

アルゴリズムのアプローチ

この問題は、再帰に頼らない反復的なアプローチで解くことができます。手順は以下のとおりです。

  1. スタックを用いて、二分木を前順走査(先行順走査)します。
  2. 走査の過程で、各ノードの親ノードへのポインタをマップ(std::map)に記録していきます。
  3. 走査中にリーフノード(左右どちらの子も持たないノード)を見つけたら、親ポインタをさかのぼることで、ルートノードからの経路を簡単に出力できます。

サンプルコード

#include <bits/stdc++.h>
using namespace std;
struct Node{
    int data;
    struct Node *left, *right;
};
// 新しいノードを作成する
Node* create_node(int data){
    Node* node = new Node;
    node->data = data;
    node->left = node->right = NULL;
    return node;
}
// ルートからリーフへの経路を出力する
void print_cpath(Node* curr, map<Node*, Node*> parent){
    stack<Node*> nodes_stack;
    while (curr){
        nodes_stack.push(curr);
        curr = parent[curr];
    }
    while (!nodes_stack.empty()){
        curr = nodes_stack.top();
        nodes_stack.pop();
        cout << curr->data << " ";
    }
    cout << endl;
}
// 前順走査を実行する
void preorder_traversal(Node* root){
    if (root == NULL)
        return;
    stack<Node*> nodeStack;
    nodeStack.push(root);
    map<Node*, Node*> parent;
    parent[root] = NULL;
    while (!nodeStack.empty()){
        Node* current = nodeStack.top();
        nodeStack.pop();
        if (!(current->left) && !(current->right))
            print_cpath(current, parent);
        if (current->right){
            parent[current->right] = current;
            nodeStack.push(current->right);
        }
        if (current->left){
            parent[current->left] = current;
            nodeStack.push(current->left);
        }
    }
}
int main(){
    Node* root = create_node(101);
    root->left = create_node(82);
    root->right = create_node(23);
    root->left->left = create_node(34);
    root->left->right = create_node(55);
    root->right->left = create_node(29);
    preorder_traversal(root);
    return 0;
}

出力

101 82 34
101 82 55
101 23 29

計算量

時間計算量は、前順走査ですべてのノードを訪問するO(N)に加え、マップへの挿入・参照やリーフごとの経路出力のコストが加わるため、全体としてO(N log N)となります。また、親ポインタを記録するマップと走査用のスタックが必要なため、空間計算量はO(N)です。

再帰を使わないこの手法は、木が非常に深くなる場合にスタックオーバーフローのリスクを避けたい場面で特に有効です。

  1. C++で再帰を使わずに二分木のルートからリーフへの経路を出力するプログラム

    このチュートリアルでは、与えられた二分木において、ルートノードからすべてのリーフノード(葉ノード)への経路を出力するプログラムを、C++で再帰を使わずに実装する方法を解説します。例として、次のような二分木を考えてみましょう。この二分木には、34・55・29という3つのリーフノードが存在します。したがって、ルートノードからリーフノードへの経路は3つあることになります。アルゴリズムのアプローチこの問題は、再帰に頼らない反復的なアプローチで解くことができます。手順は以下のとおりです。スタックを用いて、二分木を前順走査(先行順走査)します。走査の過程で、各ノードの親ノードへのポインタをマップ(std:

  2. C++で再帰を使わずに二分木のルートからリーフまでのパスを出力する方法

    二分木が与えられたとき、ルートからリーフ(葉)までの複数のパスをすべて出力する必要があります。しかし、ここでの課題は再帰を使用せずに実装することです。通常、木の探索には再帰がよく使われますが、今回は制約として再帰が使えないため、反復処理(イテレーティブな方法)で木を走査します。そのために、STLのmapを活用します。このマップには各ノードとその親ノードの対応関係を格納し、レベル順走査(またはスタックを用いた走査)によってリーフノードを検出した時点で、親へのポインタをたどることでルートからリーフまでのパスを出力できます。上記の二分木の場合、ルートからリーフまで到達するためのパスは以下のように複数