C++で二分木の中の最大BST(二分探索木)を見つける方法
二分木では、各子ノードが持てる子は最大2つ(左と右)です。木構造はデータを階層的に表現するシンプルな仕組みであり、その中でも二分探索木(BST:Binary Search Tree)は、次の条件を満たす特殊な二分木として定義されます。
- 左の子ノードは、親ノードより小さい値を持つ
- 右の子ノードは、親ノードより大きい値を持つ
本記事では、「与えられた二分木の中に含まれる最大のBST(部分木)を見つけ、そのサイズを返す」関数をC++で実装する方法を解説します。二分木そのものがBSTになっている場合は、二分木全体のサイズがそのまま答えになります。
問題の例
例1
入力:
10
/\
5 15
/ \ \
1 8 7この場合、BSTとなっている部分木のうち最大のものはサイズ「3」です。したがって、戻り値は3となります。
例2
入力:
52
/ \
37 67
/ \ / \
12 27 57 77
/\
72 87出力:
5
この例では、BSTを構成できる最大の部分木のノード数は5です。
最大のBSTを見つけるための考え方
あるノード x を根とする二分木がBSTであるためには、次の条件がすべて成立している必要があります。
- 左部分木には、親ノードより小さいデータを持つノードのみが存在する
- 右部分木には、親ノードより大きいデータを持つノードのみが存在する
- 左右どちらの部分木も、それぞれBSTである
アルゴリズム
二分木の根から処理を開始し、再帰を用いて各ノードを走査します。現在のノード「ROOT」に対しては、以下の手順を実行します。
- 現在のノードを根とする部分木が有効なBSTであれば、そのサイズを返す
- BSTでなければ、左部分木と右部分木それぞれの中で最大のBSTを探し、大きい方を返す
この方法では、各ノードごとにBST判定とサイズ計算を行うため、最悪の場合の時間計算量はO(n²)になります。ボトムアップ方式で情報を集約すればO(n)に改善できますが、まずは理解しやすい再帰によるシンプルな実装を見てみましょう。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
struct Node {
int data;
struct Node *left;
struct Node *right;
};
struct Node *newNode(int data) {
struct Node *node = new Node;
node->data = data;
node->left = node->right = NULL;
return (node);
}
struct Detail {
int size;
int max;
int min;
int ans;
bool isBST;
};
bool isBST(Node *root, int min, int max) {
if (root == NULL) {
return true;
}
if (root->data < min || root->data > max) {
return false;
}
return isBST(root->left, min, root->data - 1) &&
isBST(root->right, root->data + 1, max);
}
int size(Node *root) {
if (root == NULL) {
return 0;
}
return 1 + size(root->left) + size(root->right);
}
int largestBST(Node *root) {
// 現在の部分木がBSTかどうかを判定
if (isBST(root, INT_MIN, INT_MAX) == true) {
return size(root);
}
// BSTでなければ、左右の部分木から最大のBSTを探す
return max(largestBST(root->left), largestBST(root->right));
}
int main() {
struct Node *root = newNode(67);
root->left = newNode(72);
root->right = newNode(77);
root->left->left = newNode(57);
printf("Size of the largest BST is %d", largestBST(root));
return 0;
}実行結果
Size of the largest BST is 2
まとめ
この記事では、二分木と二分探索木(BST)の基本概念を確認し、再帰を活用して与えられた二分木の中から最大のBSTを見つける方法を学びました。各ノードについて「その部分木がBSTかどうか」を再帰的に判定し、BSTであればサイズを、そうでなければ左右の部分木の結果の最大値を返すことで、目的の答えを得られます。シンプルで理解しやすい手法ですが、ノード数が多い場合は計算量に注意し、必要に応じてO(n)のボトムアップ方式への最適化も検討するとよいでしょう。
-
C++で二分木の前順走査における先行ノード(Preorder Predecessor)を求める方法
問題の概要 この問題では、二分木とあるノードの値が与えられ、そのノードの前順走査における先行ノード(Preorder Predecessor)を出力することが求められます。 用語の整理 二分木(Binary Tree)とは、各ノードが最大2つの子ノードを持つことができる特殊な木構造のことです。 前順走査(Preorder Traversal)は、木のノードを巡回する方法の一つで、「根ノード → 左の子 → 右の子」の順に訪問していきます。 前順先行ノードとは、前順走査において対象ノードの直前に訪問されるノードのことを指します。 具体例 次の例で問題を確認してみましょう。 入力: 1 出力:
-
C++で二分木の前順走査における後続ノードを求める方法
この問題では、二分木とあるノードの値が与えられ、そのノードの前順走査(プレオーダー)における後続ノードを出力することが求められます。基本用語の整理二分木(Binary Tree):各ノードが最大2つの子ノードを持つことができる特別な木構造です。前順走査(Preorder Traversal):木のノードを巡回する方法の1つで、「根ノード → 左の子 → 右の子」の順に訪問します。前順走査における後続ノード:前順走査の順序において、対象ノードの直後に現れるノードのことです。問題例具体例を見て、問題を理解しましょう。入力: 9 出力: 0 説明: この木の前順走査は「5 9 0 1 2 5」の順に