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

C++で二分木の最長ジグザグパスを求めるアルゴリズムと実装

二分木におけるジグザグパスとは

二分木のルートが与えられたとき、ジグザグパス(ZigZag Path)は以下のように定義されます。

  • 二分木内の任意のノードと、方向(右または左)を選択します。
  • 現在の方向が「右」であれば現在のノードの右の子へ移動し、「左」であれば左の子へ移動します。
  • その後、方向を右から左へ、または左から右へ切り替えます。
  • 木の中でこれ以上移動できなくなるまで、2番目と3番目の手順を繰り返します。

ここでジグザグパスの長さは「訪問したノード数 − 1」として定義されます(単一ノードの場合、長さは0です)。この記事では、与えられた二分木に含まれる最長のジグザグパスを見つける方法を解説します。

例えば、次のような二分木が与えられた場合を考えてみましょう。

C++で二分木の最長ジグザグパスを求めるアルゴリズムと実装

この場合の出力は 3 となります。これは(右 → 左 → 右)というパスが最長だからです。

解法のアプローチ

この問題を解くために、以下の手順に従います。

  • dfs() というメソッドを定義します。引数としてルートノードとブール値 leftB を受け取ります。
  • ルートが null の場合は -1 を返します。
  • ルートが木の中で唯一のノードである場合は 0 を返します。
  • leftV := dfs(ルートの左の子, true) および rightV := dfs(ルートの右の子, false) を計算します。
  • ret := max(ret, 1 + max(leftV, rightV)) として結果を更新します。
  • leftB が true の場合は 1 + rightV を返し、そうでなければ 1 + leftV を返します。
  • メインメソッドでは ret := 0 を初期化します。
  • dfs(root, true)dfs(root, false) を呼び出します。
  • ret を返します。

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;
}
class Solution {
   public:
   int ret;
   int dfs(TreeNode* root, bool leftB){
      if(!root) return -1;
      if(!root->left && !root->right) return 0;
      int leftV = dfs(root->left, true);
      int rightV = dfs(root->right, false);
      ret = max(ret, 1 + max(leftV, rightV));
      if(leftB) return 1 + rightV;
         return 1 + leftV;
   }
   int longestZigZag(TreeNode* root) {
      ret = 0;
      dfs(root, true);
      dfs(root, false);
      return ret;
   }
};
main(){
   vector<int> v = {1,NULL,1,1,1,NULL,NULL,1,1,NULL,1,NULL,NULL,NULL,1,NULL,1};
   TreeNode *root = make_tree(v);
   Solution ob;
   cout << (ob.longestZigZag(root));
}

入力

[1,null,1,1,1,null,null,1,1,null,1,null,null,null,1,null,1]

出力

3

まとめ

このアルゴリズムは深さ優先探索(DFS)を活用し、各ノードから左右交互に進むパスの長さを再帰的に計算することで、二分木全体の中から最長のジグザグパスを効率的に見つけ出します。時間計算量は O(n)、空間計算量は木の高さに依存する O(h) となります。

  1. C++で二分木における2つの葉ノード間の最大パス合計を求める方法

    問題の概要 この問題では、各ノードが値を持つ二分木が与えられます。私たちのタスクは、二分木における2つの葉ノード(リーフノード)間の最大パス合計を求めるプログラムを作成することです。 ここで求めるのは、値の合計が最大になるような、ある葉ノードから別の葉ノードへのパスです。この最大合計パスには、ルートノードが含まれる場合もあれば、含まれない場合もあります。 二分木(Binary Tree)とは、各ノードが最大2つの子ノードを持つことができる木構造のデータ構造です。それぞれの子ノードは「左の子(left child)」と「右の子(right child)」と呼ばれます。 具体例 以下のような二分木

  2. Pythonでジグザグラベル付き二分木のパスを求める方法

    ジグザグラベル付き二分木とはすべてのノードが2つの子を持つ無限の二分木を考えます。この木では、ノードに行順にラベルが付けられており、奇数行(1行目・3行目・5行目…)は左から右へ、偶数行(2行目・4行目・6行目…)は右から左へとラベルが振られます。そのため、木全体は次のようなジグザグ状の構造になります。このような木において、あるノードのラベルが与えられたとき、ルートからそのノードまでのパス上のラベル一覧を求めるのが本問題です。たとえば入力が label = 14 の場合、出力は [1, 3, 4, 14] となります。解法のアプローチ基本的なアイデアは、「ジグザグのラベル順序に従って配列(tr