C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で二分木の「良いノード」を数えるアルゴリズム

問題の概要

二分木が与えられたとき、木の中のノードXは、ルートからXへの経路上に、Xより大きい値を持つノードが存在しない場合に「良いノード(good node)」と定義されます。この記事では、二分木に含まれる良いノードの数を求める方法を解説します。

例えば、次のような二分木が入力として与えられたとします。

C++で二分木の「良いノード」を数えるアルゴリズム

この場合、出力は 4 となります。色付きで示されたノードが「良いノード」に該当します。

解決アプローチ

この問題は、深さ優先探索(DFS)を使うことで効率的に解くことができます。具体的な手順は以下の通りです。

DFS関数の定義

  • dfs() 関数を定義します。引数としてノード(node)と現在までの最大値(val)を受け取ります。
  • node が null の場合は何もせず return します。
  • val が現在のノードの値以下であれば、答え(ret)に1を加算します。そうでなければ加算しません。
  • dfs() を再帰的に呼び出し、左の子ノードには val とノードの値の大きい方を渡します。
  • 同様に、右の子ノードに対しても再帰的に dfs() を呼び出します。

メイン処理の流れ

  • カウンタ変数 ret を 0 で初期化します。
  • dfs(root, -∞) を実行します。初期値に負の無限大を設定することで、ルートノードは必ず「良いノード」と判定されます。
  • 最後に ret を返します。

C++での実装例

それでは、実際のコードを見て理解を深めましょう。

#include <bits/stdc++.h>
using namespace std;
class TreeNode{
public:
   int val;
   TreeNode *left, *right;
   TreeNode(int data){
      val = data;
      left = NULL;
      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;
}
class Solution {
public:
   int ret;
   void dfs(TreeNode* node, int val){
      if (!node)
         return;
      ret += val <= node->val;
      dfs(node->left, max(val, node->val));
      dfs(node->right, max(val, node->val));
   }
   int goodNodes(TreeNode* root){
      ret = 0;
      dfs(root, INT_MIN);
      return ret;
   }
};
main(){
   Solution ob;
   vector<int> v = {3,1,4,3,NULL,1,5};
   TreeNode *root = make_tree(v);
   cout << (ob.goodNodes(root));
}

入力例

{3,1,4,3,NULL,1,5}

出力

4

まとめ

このアルゴリズムは、各ノードを一度だけ訪問するため、計算量はO(N)(Nはノード数)、再帰によるスタック使用を含めた空間計算量は最悪情况下O(N)となります。DFSで経路上の最大値を引き継ぐというシンプルな発想により、効率的に「良いノード」を判定できる点がポイントです。

  1. C++で二分木のすべての内部ノードを出力する方法

    この記事では、与えられた二分木からすべての内部ノードを見つけて出力する方法を解説します。 二分木と内部ノードとは 二分木(バイナリツリー)とは、各ノードが最大2つの子ノードを持つことができる木構造のデータ構造です。ノードは子をまったく持たないこともあれば、1つだけ持つこと、2つ持つこともあります。 内部ノードとは、少なくとも1つの子ノードを持つノードのことを指します。言い換えると、葉ノード(子を持たないノード)以外のノードがすべて内部ノードです。 具体例 次のような二分木を考えてみましょう。 この木の場合、子ノードを持っているのは 7、4、9 の3つのノードなので、出力は以下のようになります

  2. 【C++】部分木にK個の葉を持つ二分木のノードをすべて出力するアルゴリズム

    問題概要 この問題では、二分木と整数Kが与えられ、「自分の部分木(子孫ノード)の中にちょうどK個の葉を持つ」ノードをすべて見つけて出力することが求められます。 二分木とは、各ノードが持てる子ノードの数が最大2個(0個・1個・2個)である特別な木構造のことです。 葉ノードとは、二分木において子を一切持たない、木の末端に位置するノードのことです。 具体例で理解する 次のような二分木を考えてみましょう。 A / \ B K / \ / \ N S T E / \ /