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

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

問題の概要

この問題では、各ノードが値を持つ二分木が与えられます。私たちのタスクは、二分木における2つの葉ノード(リーフノード)間の最大パス合計を求めるプログラムを作成することです。

ここで求めるのは、値の合計が最大になるような、ある葉ノードから別の葉ノードへのパスです。この最大合計パスには、ルートノードが含まれる場合もあれば、含まれない場合もあります。

二分木(Binary Tree)とは、各ノードが最大2つの子ノードを持つことができる木構造のデータ構造です。それぞれの子ノードは「左の子(left child)」と「右の子(right child)」と呼ばれます。

具体例

以下のような二分木を考えてみましょう。

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

入力 − 二分木…

出力 − 24

説明 − 葉ノード 2 から 9 へのパスが最大の合計を与えます。その計算は (2 + 5 + 6 - 2 + 4 + 9) = 24 となります。

解法のアプローチ

この問題を解くためには、木を走査(トラバーサル)しながら、現在のノードに対する左部分木・右部分木それぞれの最大合計を記録していきます。同時に、それまでに見つかった「2つの葉ノード間の最大パス」も追跡します。

すべてのノードについて、その部分木における可能な限り最大の「葉から葉へのパス」を求めます。そして、それをこれまでの全体の最大パスと比較し、大きい方の値をグローバルな最大パス合計として保存します。

例を使った解法の確認

先ほどの例をもとに、解法をより深く理解しましょう。

初期状態のグローバル最大合計 = 6(パス 2 → 5 → -1)

次に、6 をルートノードとした場合を確認します。

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

左部分木の場合 −

葉ノードまでのパスの合計は 7 と 4 です。

最大値は 7(パス 5 → 2)となります。

右部分木の場合 −

葉ノードまでのパスの合計は 5 で、パス(1 → -3 → 7)が1つの候補になります。

以上より、葉ノード間のパスの合計は −

左部分木における「葉からルート(6)までの最大合計」+ ルート + 右部分木における「葉からルート(6)までの最大合計」= 7 + 6 + 5 = 18

グローバル最大パス合計(6)と比較すると、新しいグローバル最大パス合計は 18 に更新されます。

C++での実装例

以下は、2つの葉ノード間の最大パス合計を求めるC++プログラムです。

#include <bits/stdc++.h>
using namespace std;
struct Node{
    int data;
    struct Node* left, *right;
};
struct Node* insertNode(int data){
    struct Node* node = new(struct Node);
    node->data = data;
    node->left = node->right = NULL;
    return (node);
}
int max(int a, int b)
{ return (a >= b)? a: b; }
int maxPathSumLeaf(struct Node *root, int &maxSum){
    if (root==NULL) return 0;
    if (!root->left && !root->right) return root->data;
    int leftSubTree = maxPathSumLeaf(root->left, maxSum);
    int rightSubTree = maxPathSumLeaf(root->right, maxSum);
    if (root->left && root->right){
        maxSum = max(maxSum, leftSubTree + rightSubTree + root->data);
        return max(leftSubTree, rightSubTree) + root->data;
    }
    return (!root->left)? rightSubTree + root->data: leftSubTree + root->data;
}
int main(){
    struct Node *root = insertNode(-2);
    root->left = insertNode(6);
    root->right = insertNode(4);
    root->left->left = insertNode(5);
    root->left->right = insertNode(1);
    root->left->left->left = insertNode(2);
    root->left->left->right = insertNode(-1);
    root->left->right->left = insertNode(-3);
    root->left->right->left->left = insertNode(7);
    root->right->left = insertNode(9);
    root->right->right = insertNode(3);
    int maxSum = INT_MIN;
    maxPathSumLeaf(root, maxSum);
    cout<<"指定された二分木における2つの葉ノード間の最大パス合計は "<<maxSum;
    return 0;
}

出力結果

指定された二分木における2つの葉ノード間の最大パス合計は 24

計算量について

このアルゴリズムの時間計算量は O(n) です。これは、木に含まれる各ノードをちょうど1回ずつ訪問するためです。また、空間計算量は再帰呼び出しのスタックに依存し、木の高さを h とすると O(h) となります。平衡な二分木であれば O(log n)、最悪の場合(線形に連なる木)は O(n) になります。

  1. C++で二分木の最大垂直和を求める方法

    はじめに二分木が与えられたとき、垂直順序走査における各垂直列のノード値の合計を計算し、その中から最大値を求めて出力するのが本記事の課題です。例として、以下のような二分木を考えてみましょう。この二分木を垂直順序走査すると、各列の合計は次のようになります。4 2 1 + 5 + 6 = 12 3 + 8 = 11 7 9各列の合計の中で最大となるのは 12 です。アルゴリズムの考え方アプローチはシンプルです。幅優先探索(BFS)を用いて垂直順序走査を行い、各ノードに水平距離を割り当てます。ルートの水平距離を 0 とし、左に移動するごとに -1、右に移動するごとに +1 とします。同じ水平距離を持つ

  2. Pythonで解く二分木の最大パス和(Maximum Path Sum)

    問題の概要空でない二分木が1つ与えられます。この木における「最大パス和」を求めるのが目的です。ここでいうパスとは、あるノードを起点として、親子関係で結ばれたノードをたどり任意のノードへ至るまでのノード列のことです。パスには少なくとも1つのノードが含まれている必要がありますが、必ずしも根(ルート)ノードを通る必要はありません。例として、次のような二分木が入力された場合を考えてみましょう。この場合の出力は 32 となります。アルゴリズムの考え方各ノードを「パスの折り返し地点」として捉えるのがポイントです。あるノードを頂点とするパスの和は、「左部分木からの最大寄与 + 右部分木からの最大寄与 + そ