C++で二分木の前順走査における後続ノードを求める方法
この問題では、二分木とあるノードの値が与えられ、そのノードの前順走査(プレオーダー)における後続ノードを出力することが求められます。
基本用語の整理
二分木(Binary Tree):各ノードが最大2つの子ノードを持つことができる特別な木構造です。
前順走査(Preorder Traversal):木のノードを巡回する方法の1つで、「根ノード → 左の子 → 右の子」の順に訪問します。
前順走査における後続ノード:前順走査の順序において、対象ノードの直後に現れるノードのことです。
問題例
具体例を見て、問題を理解しましょう。
入力: 9 出力: 0 説明: この木の前順走査は「5 9 0 1 2 5」の順に行われます。したがって、9の後続ノードは0となります。
解法のアプローチ
1. 素朴な解法
二分木全体の前順走査をあらかじめ求めておき、与えられた値の次に現れる要素を出力する方法です。考え方はシンプルですが、木全体を走査する必要があるため、効率はあまり良くありません。
2. 効率的な解法
ノードの位置に着目し、後続ノードを直接求める方法です。以下のルールに従って探索します。
- ノードに左の子が存在する場合 → その左の子が後続ノード
- ノードが葉であり、親の左の子である場合 → 兄弟ノード(親の右の子)が後続ノード
- ノードが葉であり、親の右の子である場合 → 「自分が左の子であるような祖先」を上方向へ探索し、その祖先の右の子が後続ノード。該当する祖先が存在しない場合、後続ノードは存在しません(NULL)
この方法では木の高さ分の探索で済むため、時間計算量はO(h)(hは木の高さ)と非常に効率的です。
実装例(C++)
以下のプログラムで、解法がより明確になります。
#include <iostream>
using namespace std;
struct Node {
struct Node *left, *right, *parent;
int key;
};
Node* insertNode(int key){
Node* temp = new Node;
temp->left = temp->right = temp->parent = NULL;
temp->key = key;
return temp;
}
Node* preOrderSuccessorNode(Node* root, Node* n){
if (n->left)
return n->left;
Node *curr = n, *parent = curr->parent;
while (parent != NULL && parent->right == curr) {
curr = curr->parent;
parent = parent->parent;
}
if (parent == NULL)
return NULL;
return parent->right;
}
int main(){
Node* root = insertNode(99);
root->parent = NULL;
root->left = insertNode(4);
root->left->parent = root;
root->left->left = insertNode(18);
root->left->left->parent = root->left;
root->left->right = insertNode(50);
root->left->right->parent = root->left;
root->right = insertNode(26);
root->right->parent = root;
root->right->left = insertNode(5);
root->right->left->parent = root->right;
root->right->right = insertNode(10);
root->right->right->parent = root->right;
Node* preOrder = preOrderSuccessorNode(root, root->left->right);
if (preOrder) {
cout<<"Preorder successor of "<<root->left->right->key<<" is "<<preOrder->key;
} else {
cout<<"Preorder successor of "<<root->left->right->key<<" is NULL";
}
return 0;
}出力
Preorder successor of 50 is 26
この例では、ノード50は葉であり、親ノード4の右の子にあたります。そこで「自分が左の子であるような祖先」を上方向へ辿ると根ノード99が該当し、その右の子である26が50の後続ノードとして返されます。
-
C++で二分木の前順走査における先行ノード(Preorder Predecessor)を求める方法
問題の概要 この問題では、二分木とあるノードの値が与えられ、そのノードの前順走査における先行ノード(Preorder Predecessor)を出力することが求められます。 用語の整理 二分木(Binary Tree)とは、各ノードが最大2つの子ノードを持つことができる特殊な木構造のことです。 前順走査(Preorder Traversal)は、木のノードを巡回する方法の一つで、「根ノード → 左の子 → 右の子」の順に訪問していきます。 前順先行ノードとは、前順走査において対象ノードの直前に訪問されるノードのことを指します。 具体例 次の例で問題を確認してみましょう。 入力: 1 出力:
-
Pythonで二分木の先行順走査(プレオーダートラバーサル)を実装する方法
Pythonでの二分木の先行順走査(プレオーダートラバーサル)とは二分木が与えられたとき、その木を先行順走査(プレオーダートラバーサル)で巡回した結果を返すことを考えます。先行順走査とは、「根のノード → 左部分木 → 右部分木」の順序でノードを訪問する木構造の基本的な走査手法です。例えば、次のような二分木があるとします。この木に対する先行順走査の結果は [3, 9, 20, 15, 7] となります。アルゴリズムの手順ここでは、再帰を使わずにスタックを利用した反復的なアプローチで問題を解きます。手順は以下の通りです。結果を格納するための空リスト res と、スタックとして使用する空リスト s