C++で二分木の半ノード(ハーフノード)を数える方法【反復・再帰の両アプローチ】
本記事では、二分木(バイナリツリー)に含まれる「半ノード(ハーフノード)」の数を、反復処理と再帰処理の2つのアプローチで求める方法を解説します。
半ノードとは?
半ノードとは、子を1つだけ持ち、もう片方の子がNULL(空)になっているノードのことです。なお、子をまったく持たない葉ノードは半ノードには含まれない点に注意してください。
二分木はデータの格納に使われる特殊なデータ構造です。各ノードが最大2つの子を持つという制約があり、ソート済み配列並みの高速な検索と、連結リスト並みの高速な挿入・削除の両方を実現できるというメリットがあります。
二分木の基本的な構造は以下の通りです。

具体例
入力:

出力: カウントは 2
解説: この二分木では、40 と 50 の2つのノードが子をちょうど1つだけ持つ半ノードです。それ以外のノードは、子を2つ持つか、まったく持たないかのどちらかです。
反復処理(キューを使ったレベル順走査)による実装
プログラムの考え方
- データ部と左右ポインタを持つノード構造体を定義します。
- 二分木にノードを挿入する関数を作成します。
- 半ノードをカウントする関数を作成します。
- 関数内で、まず !node(木が空)の場合は 0 を返します。
- 半ノードの個数を保持する一時変数 count を宣言します。
- キュー型の変数 qu を用意します。
- qu.push(node) でルートノードをキューに追加します。
- qu.empty() が false の間、ループを回します。
- Node 型の一時変数 temp を queue.front() で初期化します。
- qu.pop() で要素を取り出します。
- (!temp->left && temp->right) || (temp->left && !temp->right) の条件を満たせば、count を1増やします。
- temp->left が NULL でなければ qu.push(temp->left) を実行します。
- 最後に count を返し、結果を出力します。
サンプルコード
// 二分木の半ノードを数えるプログラム(反復版)
#include <iostream>
#include <queue>
using namespace std;
struct Node{
int data;
struct Node* left, *right;
};
// 半ノードの個数を取得する関数
int halfcount(struct Node* node){
// 木が空の場合
if (!node)
return 0;
int result = 0; // 半ノードのカウントを初期化
// ルートからレベル順走査(BFS)を行う
queue<Node *> myqueue;
myqueue.push(node);
while (!myqueue.empty()){
struct Node *temp = myqueue.front();
myqueue.pop();
if ((!temp->left && temp->right) || (temp->left && !temp->right)){
result++;
}
if (temp->left != NULL){
myqueue.push(temp->left);
}
if (temp->right != NULL){
myqueue.push(temp->right);
}
}
return result;
}
/* 指定されたデータを持ち、左右ポインタがNULLの新規ノードを確保する */
struct Node* newNode(int data){
struct Node* node = new Node;
node->data = data;
node->left = node->right = NULL;
return (node);
}
int main(void){
struct Node *root = newNode(10);
root->left = newNode(20);
root->right = newNode(30);
root->left->left = newNode(40);
root->left->right = newNode(50);
root->left->left->right = newNode(60);
root->left->right->right = newNode(70);
cout <<"count is: "<<halfcount(root);
return 0;
}実行結果
上記のコードを実行すると、次の出力が得られます。
count is: 2
再帰処理による実装
プログラムの考え方
- データ部と左右ポインタを持つノード構造体を定義します。
- 二分木にノードを挿入する関数を作成します。
- 半ノードをカウントする関数を作成します。
- 関数内で、root == NULL の場合は 0 を返します。
- 半ノードの個数を保持する一時変数 count を宣言します。
- (root->left == NULL && root->right != NULL) || (root->left != NULL && root->right == NULL) の条件を満たせば、count を1増やします。
- count = count + 左部分木の再帰呼び出し + 右部分木の再帰呼び出し とします。
- 最後に count を返し、結果を出力します。
サンプルコード
// 二分木の半ノードを数えるプログラム(再帰版)
#include <bits/stdc++.h>
using namespace std;
// 二分木のノードはデータ、左の子へのポインタ、右の子へのポインタを持つ
struct Node{
int data;
struct Node* left, *right;
};
int halfcount(struct Node* root){
if (root == NULL)
return 0;
int result = 0;
if ((root->left == NULL && root->right != NULL) || (root->left != NULL && root->right ==
NULL)){
result++;
}
result += (halfcount(root->left) + halfcount(root->right));
return result;
}
/* 指定されたデータを持ち、左右ポインタがNULLの新規ノードを確保する */
struct Node* newNode(int data){
struct Node* node = new Node;
node->data = data;
node->left = node->right = NULL;
return (node);
}
int main(){
struct Node *root = newNode(10);
root->left = newNode(20);
root->right = newNode(30);
root->left->left = newNode(40);
root->left->right = newNode(50);
root->left->left->right = newNode(60);
root->left->right->right = newNode(70);
cout <<"count is: "<<halfcount(root);
return 0;
}実行結果
上記のコードを実行すると、次の出力が得られます。
count is: 2
まとめ
どちらのアプローチでも同じ結果が得られます。反復法はキューを使った幅優先探索(BFS)で木全体を走査するため、深い木でもスタックオーバーフローの心配が少なく、再帰法はコードが簡潔で直感的に理解しやすいという特徴があります。それぞれの手法の計算量はいずれも O(n)、必要な領域は反復法ではキューのサイズ(最悪 O(n))、再帰法では呼び出しスタックの深さ(最悪 O(n))となります。
-
C++で二分木のすべての内部ノードを出力する方法
この記事では、与えられた二分木からすべての内部ノードを見つけて出力する方法を解説します。 二分木と内部ノードとは 二分木(バイナリツリー)とは、各ノードが最大2つの子ノードを持つことができる木構造のデータ構造です。ノードは子をまったく持たないこともあれば、1つだけ持つこと、2つ持つこともあります。 内部ノードとは、少なくとも1つの子ノードを持つノードのことを指します。言い換えると、葉ノード(子を持たないノード)以外のノードがすべて内部ノードです。 具体例 次のような二分木を考えてみましょう。 この木の場合、子ノードを持っているのは 7、4、9 の3つのノードなので、出力は以下のようになります
-
与えられた二分木の後順(ポストオーダー)再帰走査を実行するC++プログラム
木構造の走査(トラバーサル)はグラフ走査の一種であり、木の中の各ノードを正確に一度だけ訪問して確認・出力する操作を指します。二分探索木の後順走査(ポストオーダー走査)では、木の各ノードを「左 → 右 → 根」の順序で訪問します。二分木の後順走査の例を以下に示します。次のような二分木が与えられたとします。この場合、後順走査の結果は次のようになります。後順走査の出力:1 5 4 8 6後順再帰走査を行うC++プログラム後順(ポストオーダー)再帰走査を実行するプログラムは以下の通りです。#include<iostream> using namespace std; struct node