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

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

二分木が与えられたとき、ルートからリーフ(葉)までの複数のパスをすべて出力する必要があります。しかし、ここでの課題は再帰を使用せずに実装することです。

通常、木の探索には再帰がよく使われますが、今回は制約として再帰が使えないため、反復処理(イテレーティブな方法)で木を走査します。そのために、STLのmapを活用します。このマップには各ノードとその親ノードの対応関係を格納し、レベル順走査(またはスタックを用いた走査)によってリーフノードを検出した時点で、親へのポインタをたどることでルートからリーフまでのパスを出力できます。

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

上記の二分木の場合、ルートからリーフまで到達するためのパスは以下のように複数存在します。

10 -> 3 -> 140
10 -> 3 -> 162
10 -> 211 -> 100
10 -> 211 -> 146

したがって、プログラムは与えられた二分木に対して、これらのパスをすべて出力しなければなりません。

アルゴリズム

START
Step 1 -> ノードの構造体を作成する
   struct Node
      struct node *left, *right
      int data
   End
Step 2 -> 新しいノードを作成する関数
   node* newnode(int data)
   node->data = data
   node->left = node->right = NULL;
   return (node)
Step 3 -> パスを計算する関数を作成する
   void calculatePath(Node* curr, map<Node*, Node*> first)
   STL stack<Node*> stk を作成
   While (curr) の間ループ
      stk.push(curr)
      curr = first[curr]
   End
   !stk.empty() の間ループ
      curr = stk.top()
      stk.pop()
      curr->data を出力
   End
Step 4 -> リーフノードを見つける関数を作成する
   void leaf(Node* root)
   IF root = NULL ならば
      Return
   End
   STL stack<Node*> stc を作成
   stc.push(root)
   STL map<Node*, Node*> prnt を作成
   prnt[root] = NULL
   !stc.empty() の間ループ
      Node* curr = stc.top()
      stc.pop()
      IF !(curr->left) && !(curr->right) ならば
         calculatePath(curr, prnt)
      End
      IF curr->right ならば
         prnt[curr->right] = curr
         stc.push(curr->right)
      End
      IF curr->left ならば
         prnt[curr->left] = curr
         stc.push(curr->left)
      End
   End
STOP

処理の流れのポイント

  • スタックによる走査: 再帰の代わりにstackを使ってノードを管理することで、深さ優先的な走査を実現しています。
  • 親情報の記録: map<Node*, Node*>に「子ノード → 親ノード」の対応を保存しておくことで、後からリーフからルート方向へ遡れるようにしています。
  • パスの出力: リーフノードが見つかったら、マップをたどってルートまで遡りながらスタックに積み、最後にスタックから取り出すことで正しい順序(ルート→リーフ)でパスを出力します。

実装例

#include <bits/stdc++.h>
using namespace std;
// ノードの構造体
struct Node{
   int data;
   struct Node *left, *right;
};
// 新しいノードを作成する関数
Node* newNode(int data){
   Node* node = new Node;
   node->data = data;
   node->left = node->right = NULL;
   return node;
}
// パスを計算して出力する関数
void calculatePath(Node* curr, map<Node*, Node*> first){
   stack<Node*> stk;
   while (curr){
      stk.push(curr);
      curr = first[curr];
   }
   while (!stk.empty()){
      curr = stk.top();
      stk.pop();
      cout << curr->data << " ";
   }
   cout << endl;
}
// リーフノードを検出してパスを出力する関数
void leaf(Node* root){
   if (root == NULL)
      return;
   stack<Node*> stc;
   stc.push(root);
   map<Node*, Node*> prnt;
   prnt[root] = NULL;
   while (!stc.empty()){
      Node* curr = stc.top();
      stc.pop();
      if (!(curr->left) && !(curr->right))
         calculatePath(curr, prnt);
      if (curr->right){
         prnt[curr->right] = curr;
         stc.push(curr->right);
      }
      if (curr->left){
         prnt[curr->left] = curr;
         stc.push(curr->left);
      }
   }
}
int main(){
   Node* root = newNode(67); // ツリーを構築するためにノードを挿入
   root->left = newNode(34);
   root->right = newNode(89);
   root->left->left = newNode(23);
   root->left->right = newNode(95);
   root->right->left = newNode(12);
   leaf(root); // leaf関数を呼び出し
   return 0;
}

出力結果

上記のプログラムを実行すると、次のような出力が生成されます。

67 34 23
67 34 95
67 89 12

このように、再帰を一切使わずにスタックとマップを組み合わせるだけで、二分木のすべてのルートからリーフへのパスを出力できることがわかります。

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

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

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

    問題の概要二分木が与えられたとき、根(ルート)から葉(リーフ)に至る複数の経路の中から、最も短い経路を見つけ出して出力するプログラムを作成します。木は左から右へと走査するため、同じ深さの最短経路が複数存在する場合は、左側にある最初に走査された最短経路を出力します。この問題は、キュー(queue)を使ったレベル順走査(幅優先探索・BFS)で各レベルを順にたどることで解くことができます。BFSは浅い階層から順に探索を進めるため、最初に見つかった葉への経路が、すなわち根から葉への最短経路となります。上図の二分木では、根から葉への経路として以下のものが考えられます。10 -> 3(すべての経路の