C++でツリーノードを削除する:合計値が0の部分木を除去するアルゴリズム
問題概要
根がノード0であるような木構造を考えます。この木には、次の情報が与えられています。
- ノードの総数:
nodes - i番目のノードの値:
value[i] - i番目のノードの親:
parent[i]
求めたいのは、「ノードの値の合計が0になる部分木」をすべて削除した後、木に残っているノードの個数です。
たとえば、下図のような木を考えてみましょう。

ノードは全部で7つありますが、出力は2になります。これは、値が0であるノード3を根とする部分木と、ノード2を根とする部分木(4 + (-2) + (-1) + (-1) = 0)が削除対象となり、最終的に残るのがノード0とノード1だけだからです。
解法の考え方
この問題は、深さ優先探索(DFS)を使うことで効率的に解けます。各ノードについて「そのノードを根とする部分木の値の合計」と「部分木に含まれるノード数」を計算し、合計が0であればそのノード数ぶんだけ答えから減算していきます。具体的な手順は以下の通りです。
childrenというマップを作成します。dfs()メソッドを定義します。引数はノード番号、value配列、隣接リスト形式のgraphです。tempをペア (value[node], 1) で初期化します。first は部分木の値の合計、second は部分木のノード数を表します。- i を 0 から graph[node] のサイズ未満まで繰り返します。
temp2 := dfs(graph[node][i], value, graph)を呼び出します。temp.firstにtemp2.firstを、temp.secondにtemp2.secondをそれぞれ加算します。
temp.firstが 0 の場合、ansからtemp.secondを減算し、temp.secondを 0 に設定します。tempを返します。- メイン側では、nodes、parent 配列、value 配列を受け取ります。
n := value配列の要素数とします。ans := nで初期化します。- サイズ n+1 の配列
graphを定義します。 - i を 1 から n-1 まで繰り返し、
graph[parent[i]]に i を挿入して隣接リストを構築します。 dfs(0, value, graph)を呼び出します。ansを返します。
C++での実装例
それでは、実際の実装を見ていきましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
map <int, int> children;
int ans;
pair <int, int> dfs(int node, vector<int>& value, vector <int> graph[]){
pair <int, int> temp = {value[node], 1};
for(int i = 0; i < graph[node].size(); i++){
pair <int, int> temp2 = dfs(graph[node][i], value, graph);
temp.first += temp2.first;
temp.second += temp2.second;
}
if(temp.first == 0){
ans -= temp.second;
temp.second = 0;
}
return temp;
}
int deleteTreeNodes(int nodes, vector<int>& parent, vector<int>& value) {
int n = value.size();
ans = n;
children.clear();
vector < int > graph[n + 1];
for(int i = 1; i < n; i++){
graph[parent[i]].push_back(i);
}
dfs(0, value, graph);
return ans;
}
};
main(){
vector<int> v1 = {-1,0,0,1,2,2,2};
vector<int> v2 = {1,-2,4,0,-2,-1,-1};
Solution ob;
cout << (ob.deleteTreeNodes(7,v1, v2));
}入力
7 [-1,0,0,1,2,2,2] [1,-2,4,0,-2,-1,-1]
出力
2
-
C++で木の直径を求めるアルゴリズムを解説
木の直径とは無向木(undirected tree)が与えられたとき、その直径を求めることを考えます。木の直径とは、木の中で最も長い経路に含まれる辺の数のことです。ここでは、木は辺のリストとして与えられます。edges[i] = [u, v] は、ノードuとノードvをつなぐ双方向の辺を表します。また、各ノードには {0, 1, ..., edges.length} の集合からラベルが割り当てられています。例として、次のような木を考えてみましょう。この場合、最も長い経路の長さは4となるため、出力は4になります。解法のアプローチ木の直径を効率的に求めるには、DFS(深さ優先探索)を2回実行するとい
-
C++で完全二分木のノード数を効率的に数える方法
完全二分木のノード数を数える問題 完全二分木(Complete Binary Tree)が与えられたとき、その木に含まれるノードの総数を求めるのがこの問題の目的です。例えば、次のような木があった場合、出力は 6 になります。 すべてのノードを一つずつ訪問して数えれば O(n) で解けますが、完全二分木の性質をうまく利用すると、より少ない計算量でノード数を求めることができます。 解法のアプローチ ここでは再帰的なアプローチを採用します。鍵となるのは、「ある部分木について左端の高さと右端の高さが一致しているなら、その部分木は完全な満木(パーフェクトバイナリツリー)である」という完全二分木の性質で