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

C++で二分木の根から葉への最短経路を出力する方法|BFS(幅優先探索)による実装


問題の概要

二分木が与えられたとき、根(ルート)から葉(リーフ)に至る複数の経路の中から、最も短い経路を見つけ出して出力するプログラムを作成します。

木は左から右へと走査するため、同じ深さの最短経路が複数存在する場合は、左側にある最初に走査された最短経路を出力します。

この問題は、キュー(queue)を使ったレベル順走査(幅優先探索・BFS)で各レベルを順にたどることで解くことができます。BFSは浅い階層から順に探索を進めるため、最初に見つかった葉への経路が、すなわち根から葉への最短経路となります。

C++で二分木の根から葉への最短経路を出力する方法|BFS(幅優先探索)による実装

上図の二分木では、根から葉への経路として以下のものが考えられます。

10 -> 3(すべての経路の中で最短)
10 -> 211 -> 100
10 -> 211 -> 146

入力例と出力例

入力 : 10 3 211 100 146
出力 : 10 3

アルゴリズム

ステップ1:ノード構造体の作成

struct node
    struct node *left, *right
    int data
End

ステップ2:ノードを生成する関数

node* newnode(int data)
    node *temp = new node
    temp->data = data
    temp->left = temp->right = NULL
    return temp

ステップ3:経路を計算する関数

親ノードの情報を格納したマップ(prnt)を再帰的にたどり、根に向かって経路を出力します。

void path(int data, unordered_map <int,int> prnt)
    IF prnt[data] == data
        Return
    End
    path(prnt[data], prnt)
    print prnt[data]

ステップ4:左側の最短経路を求める関数

キューを使ったレベル順走査を行い、最初に見つかった葉と、そこまでの親子関係をマップに記録します。

void left(Node* root)
    STLのqueue<Node*> que を作成
    que.push(root)
    int leaf = -1
    Node* temp = NULL
    STLのunordered_map<int, int> prnt を作成
    prnt[root->data] = root->data
    While !que.empty() の間ループ
        temp = que.front()
        que.pop()
        IF !temp->left && !temp->right
            leaf = temp->data
            break
        Else
            IF temp->left
                que.push(temp->left)
                prnt[temp->left->data] = temp->data
            End
            IF temp->right
                que.push(temp->right)
                prnt[temp->right->data] = temp->data
            End
        End
    End
    path(leaf, prnt)
    print leaf

ステップ5:main() 関数内の処理

Node* root = newnode(90) で木を作成
root->left = newnode(21)
left(root) を呼び出す
終了

C++による実装例

以下は、上記のアルゴリズムをC++で実装したサンプルコードです。

#include <bits/stdc++.h>
using namespace std;
// ノードの構造体
struct Node {
    struct Node *left,*right;
    int data;
};
// 新しいノードを作成する関数
Node* newnode(int data){
    Node* temp = new Node;
    temp->data = data;
    temp->left = NULL;
    temp->right = NULL;
    return temp;
}
// 経路を出力する関数
void path(int data, unordered_map <int,int> prnt) {
    if (prnt[data] == data)
        return;
    path(prnt[data], prnt);
    cout << prnt[data] << " ";
}
// 葉への最短経路を求める関数
void left(Node* root) {
    queue<Node*> que;
    que.push(root);
    int leaf = -1;
    Node* temp = NULL;
    unordered_map<int, int> prnt;
    prnt[root->data] = root->data;
    while (!que.empty()){
        temp = que.front();
        que.pop();
        if (!temp->left && !temp->right) {
            leaf = temp->data;
            break;
        } else {
            if (temp->left){
                que.push(temp->left);
                prnt[temp->left->data] = temp->data;
            }
            if (temp->right){
                que.push(temp->right);
                prnt[temp->right->data] = temp->data;
            }
        }
    }
    path(leaf, prnt);
    cout << leaf << " ";
}
int main(){
    Node* root = newnode(90);
    root->left = newnode(21);
    root->right = newnode(32);
    root->left->left = newnode(45);
    root->right->left = newnode(52);
    root->right->right = newnode(27);
    root->left->left->left = newnode(109);
    root->left->left->right = newnode(101);
    root->right->right->left = newnode(78);
    left(root);
    return 0;
}

実行結果

上記のプログラムをコンパイルして実行すると、次の出力が得られます。

90 32 52

処理の解説

この例では、ノード「90」を根とする二分木に対して幅優先探索を行っています。探索はレベル順に進み、最初に到達した葉は「52」です。親情報を記録したマップをたどることで、経路「90 → 32 → 52」が正しい順序で出力されます。

BFSは浅い階層から順にノードを訪問する性質を持つため、「最初に見つかった葉までの経路=最短経路」という保証があります。これにより、全経路を列挙して比較する必要がなく、効率的に最短経路を求められるのが大きな利点です。

  1. C++で二分木のすべてのノードのレベルを出力する方法

    二分木(バイナリツリー)が与えられたとき、各ノードに格納されたすべてのキーについて、そのノードが属するレベル(根をレベル1として数える)を出力するのが本記事の目的です。上記の木では、ノードは次のように配置されています。10 はレベル 1 3 と 211 はレベル 2 140、162、100、146 はレベル 3特定のキーが与えられた場合、プログラムはそのキーが属するレベルを出力できなければなりません。入出力例入力: 10 3 211 140 162 100 146 出力:     10 のレベルは 1     3

  2. C++で二分木の各ノードのセットビット数を出力する方法

    二分木が与えられたとき、本記事で紹介する関数は、各ノードに格納されたキーの値を2進数に変換し、その2進表現に含まれるセットビット(1)の個数を返します。例キーとして 10、3、211、140、162、100、146 を持つ二分木を考えてみましょう。各キーの2進表現とセットビット数は以下のようになります。キー2進表現セットビット数(出力)101010230011221111010011514010001100316210100010310011001003146100100103__builtin_popcount 関数についてここでは GCC が提供する組み込み関数 __builtin_pop