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

C++で解く二分木の最長連続シーケンス II

この記事では、二分木が与えられたときに、その木の中で最長連続パス(Longest Consecutive Path)の長さを求めるアルゴリズムをC++で実装する方法を解説します。

問題の概要

二分木において、以下の条件を満たすパスのうち最も長いものの長さを見つけます。

  • パスは増加方向でも減少方向でも構いません。つまり [1, 2, 3, 4] と [4, 3, 2, 1] はどちらも有効なパスですが、[1, 2, 4, 3] のように順序が混在したものは無効です。
  • パスは子 → 親 → 子の順序であってもよく、必ずしも親から子への一方向である必要はありません。

例として、次のような二分木を考えます。

C++で解く二分木の最長連続シーケンス II

この場合、最長の連続パスは [1, 2, 3] または [3, 2, 1] となるため、出力は 3 になります。

解法のアプローチ

この問題は、各ノードを起点とした「増加連続パスの長さ」と「減少連続パスの長さ」を再帰的に計算することで解けます。手順は以下の通りです。

アルゴリズムの手順

  1. ノードを引数にとる関数 solveUtil() を定義します。
  2. ノードが null の場合は {0, 0} を返します。
  3. 左部分木・右部分木それぞれに対して solveUtil() を再帰呼び出しします。
  4. ペア temp = {1, 1} を用意します。first は増加連続の長さ、second は減少連続の長さを表します。
  5. 左の子が存在し、その値が現在のノードの値 + 1 と等しい場合:
    • temp.firsttemp.first1 + left.first の最大値で更新
    • ansanstemp.first の最大値で更新
  6. 右の子が存在し、その値が現在のノードの値 + 1 と等しい場合も同様に処理します。
  7. 左の子が存在し、その値が現在のノードの値 − 1 と等しい場合:
    • temp.secondtemp.second1 + left.second の最大値で更新
    • ansanstemp.second の最大値で更新
  8. 右の子についても同様に処理します。
  9. 子→親→子のパスを考慮するため、anstemp.first + temp.second − 1(自分自身を重複カウントしないため −1)とも比較して更新します。
  10. temp を返します。

メインメソッドでの処理

  1. ans を 0 で初期化します。
  2. solveUtil(root) を呼び出します。
  3. ans を返します。

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;
    }
};
class Solution {
public:
    int ans = 0;
    pair<int, int> solveUtil(TreeNode* node){
        if (!node) {
            return { 0, 0 };
        }
        pair<int, int> left = solveUtil(node->left);
        pair<int, int> right = solveUtil(node->right);
        pair<int, int> temp = { 1, 1 };
        if (node->left && node->left->val == node->val + 1) {
            temp.first = max(temp.first, 1 + left.first);
            ans = max(ans, temp.first);
        }
        if (node->right && node->right->val == node->val + 1) {
            temp.first = max(temp.first, 1 + right.first);
            ans = max(ans, temp.first);
        }
        if (node->left && node->left->val == node->val - 1) {
            temp.second = max(temp.second, 1 + left.second);
            ans = max(ans, temp.second);
        }
        if (node->right && node->right->val == node->val - 1) {
            temp.second = max(temp.second, 1 + right.second);
            ans = max(ans, temp.second);
        }
        ans = max({ ans, temp.first + temp.second - 1 });
        return temp;
    }
    int longestConsecutive(TreeNode* root){
        ans = 0;
        solveUtil(root);
        return ans;
    }
};
main(){
    Solution ob;
    TreeNode *root = new TreeNode(2);
    root->left = new TreeNode(1);
    root->right = new TreeNode(3);
    cout << (ob.longestConsecutive(root));
}

入力

TreeNode *root = new TreeNode(2);
root->left = new TreeNode(1);
root->right = new TreeNode(3);

出力

3

計算量について

このアルゴリズムは各ノードを一度だけ訪問するため、時間計算量は O(N)(N はノード数)、空間計算量は再帰スタックの深さ分となり、最悪の場合(木が偏っているとき)O(N)、バランスの取れた木では O(log N) となります。

  1. C++で最大二分木を構築する方法:再帰アルゴリズムと実装例を解説

    最大二分木(Maximum Binary Tree)とは? ここでは、すべての要素が一意(重複なし)である整数配列が与えられたとします。この配列から構築される「最大二分木」は、以下のように定義されます。 根(ルート)には、配列内の最大値が格納されます。 左部分木は、最大値を基準に分割された左側の部分配列から構築された最大二分木です。 右部分木は、最大値を基準に分割された右側の部分配列から構築された最大二分木です。 この定義に従って最大二分木を構築します。たとえば、入力が [3,2,1,6,0,5] の場合、構築される木は次の図のようになります。 解き方のアプローチ この問題は、再帰的な

  2. C++で二分木を二分探索木(BST)へ変換する方法を解説

    二分木(Binary Tree)とは二分木とは、木構造の各ノードが最大で2つの子ノードを持つことができる特別な木構造です。これらの子ノードは、それぞれ「左の子ノード」と「右の子ノード」と呼ばれます。シンプルな二分木の例は以下の通りです。二分探索木(BST)とは二分探索木(BST)は、以下のルールに従う特別な木構造です。左の子ノードの値は、常に親ノードの値より小さい右の子ノードの値は、常に親ノードの値より大きいすべてのノードが、それぞれ独立して二分探索木の性質を満たす二分探索木(BST)の例は以下の通りです。二分探索木は、検索や最小値・最大値の探索といった操作の計算量を削減するために用いられるデ