C++で指定された値を持つ葉ノードを削除するアルゴリズム
問題の概要
二分木と整数 target が与えられたとき、値が target と一致するすべての葉ノードを削除することを考えます。ここで重要なのは、葉ノードを削除した結果、その親ノードが新たに葉ノードになり、かつその値が target と一致する場合には、その親ノードも同様に削除しなければならないという点です。この操作は、削除できるノードがなくなるまで繰り返し行います。
例えば、下図のような二分木があり、target が 2 の場合、最終的な木は次のようになります。

解法のアプローチ
この問題は、再帰を用いた後順(ボトムアップ)処理によって効率的に解くことができます。具体的な手順は以下の通りです。
ルートノードと target を引数に取る再帰メソッド remLeaf() を定義します。
ルートが null の場合は null を返します。
left := remLeaf(ルートの左の子, target) を実行します。
right := remLeaf(ルートの右の子, target) を実行します。
left と right がどちらも null であり、かつルートの値が target と一致する場合は null を返します(このノードを削除)。
ルートの左の子 := left、右の子 := right を設定します。
ルートを返します。
子ノードから先に処理を行うことで、葉が削除されて新たに葉になった親ノードも自動的に判定・削除される仕組みになっています。
C++による実装例
以下の実装を見ると、より理解が深まるでしょう。
#include <bits/stdc++.h>
using namespace std;
class TreeNode{
public:
int val;
TreeNode *left, *right;
TreeNode(int data){
val = data;
left = right = NULL;
}
};
void insert(TreeNode **root, int val){
queue<TreeNode*> q;
q.push(*root);
while(q.size()){
TreeNode *temp = q.front();
q.pop();
if(!temp->left){
if(val != NULL)
temp->left = new TreeNode(val);
else
temp->left = new TreeNode(0);
return;
} else {
q.push(temp->left);
}
if(!temp->right){
if(val != NULL)
temp->right = new TreeNode(val);
else
temp->right = new TreeNode(0);
return;
} else {
q.push(temp->right);
}
}
}
TreeNode *make_tree(vector<int> v){
TreeNode *root = new TreeNode(v[0]);
for(int i = 1; i<v.size(); i++){
insert(&root, v[i]);
}
return root;
}
void tree_level_trav(TreeNode*root){
if (root == NULL) return;
cout << "[";
queue<TreeNode *> q;
TreeNode *curr;
q.push(root);
q.push(NULL);
while (q.size() > 1) {
curr = q.front();
q.pop();
if (curr == NULL){
q.push(NULL);
} else {
if(curr->left)
q.push(curr->left);
if(curr->right)
q.push(curr->right);
if(curr->val == 0 || curr == NULL){
cout << "null" << ", ";
} else {
cout << curr->val << ", ";
}
}
}
cout << "]"<<endl;
}
class Solution {
public:
TreeNode* removeLeafNodes(TreeNode* root, int target) {
if(!root || root->val == 0) return NULL;
TreeNode* left = removeLeafNodes(root->left, target);
TreeNode* right = removeLeafNodes(root->right, target);
if(!left && !right && root->val == target){
return NULL;
}
root->left = left;
root->right = right;
return root;
}
};
main() {
vector<int> v1 = {1,2,3,2,NULL,2,4};
TreeNode *root = make_tree(v1);
Solution ob;
tree_level_trav(ob.removeLeafNodes(root, 2));
}入力
[1,2,3,2,null,2,4] 2
出力
[1, 3, 4]
計算量について
このアルゴリズムは各ノードを一度だけ訪問するため、時間計算量は O(N)(N はノード数)、空間計算量は再帰呼び出しの深さに依存し、最悪の場合 O(N) となります。木が平衡な場合は O(log N) に抑えられます。
-
【C++】バックトラッキングでグリッドの8つのマスに1〜8の数字を条件付きで配置する方法
この記事では、図の中にある8つの丸(マス)に「1」から「8」までの数字を、「数列上で隣り合う数字同士がグリッド上でも隣接しない」という条件を満たすように配置する問題を、C++で解く方法を解説します。問題の概要たとえば、入力として次のような3×4のグリッドが与えられたとします。「0」は使用しないマス、「-1」はまだ数字が置かれていない空きマスを表します。0-1-10-1-1-1-10-1-10この場合の出力は次のようになります。 3 5 7 1 8 2 4 6この結果では、たとえば「1」と「2」、「7」と「8」のように数列で連続する数字が、グリッド上で上下左右・斜めに隣り合わないように配置
-
C++のfabs()関数の使い方を解説!絶対値を求める方法とサンプルコード
C++で数値の絶対値を求めたいときに便利なのが、<cmath>ヘッダーに定義されているfabs()関数です。この記事では、fabs()関数の基本的な使い方から、実際のサンプルコード、実行結果までをわかりやすく解説します。fabs()関数とはfabs()は、C言語およびC++の標準ライブラリに含まれる関数で、引数に渡した浮動小数点数の絶対値(absolute value)を返します。負の値を渡せば正の値に変換され、正の値はそのまま返されます。関数の宣言fabs()関数は、以下のように宣言されています。double fabs(double x)パラメータと戻り値x:絶対値を求めたい浮動