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

【C++】ルートからリーフへの経路上に、合計がルートの値と一致するノードのペアが存在するか判定する方法

この問題では、二分木(Binary Tree)が与えられます。求められているのは、「ルートからリーフ(葉)に至る経路上に、2つのノードの値の合計がルートのデータと等しくなるペアが存在するかどうか」を判定することです。

つまり、ルートノードからリーフノードまでの間にあるノードの中から2つを選んだとき、その値の合計がルートノードの値と一致するような組み合わせが存在するかをチェックします。

問題例で理解しよう

入力:

【C++】ルートからリーフへの経路上に、合計がルートの値と一致するノードのペアが存在するか判定する方法

出力: Yes

説明:

ルートノードの値は 7 です。

合計が7になるペアとして、(2, 5)(1, 6) が存在します。

解決アプローチ:ハッシュを活用した探索

木を走査しながら、ハッシュセットを使って効率的にペアを探します。

手順は以下の通りです。

1. 空のハッシュセット(hashTable)を用意します。
2. ルートの子からリーフへ向かって深さ優先探索(DFS)を行います。
3. 各ノードに到達した時点で、「ルートの値 − 現在のノードの値」に相当する値がすでにハッシュセット内に存在するかを確認します。存在すれば、合計がルートの値と一致するペアが見つかったことになります。
4. ペアが見つからなければ、現在のノードの値をハッシュセットに追加して子ノードへ進みます。
5. 子の探索が終わったら、現在のノードの値をハッシュセットから削除(バックトラック)します。これにより、常に「現在の経路上のノードの値だけ」がセットに保持されます。
6. 最後まで探索してもペアが見つからなければ false を返し、見つかれば true を返します。

解法の動作を示すプログラム

コード例

#include<bits/stdc++.h>
using namespace std;

struct Node {

    int data;
    struct Node* left, *right;
};

struct Node* newnode(int data) {

    struct Node* node = new Node;
    node->data = data;
    node->left = node->right = NULL;
    return (node);
}

bool findSumUntill(Node *node, unordered_set<int> &hashTable, int rootVal)
{
    if (node == NULL)
       return false;

    int otherVal = rootVal - node->data;
    if (hashTable.find(otherVal) != hashTable.end())
       return true;

    hashTable.insert(node->data);
    bool isFound = findSumUntill(node->left, hashTable, rootVal) || findSumUntill(node->right, hashTable, rootVal);
    hashTable.erase(node->data);

    return isFound;
}

bool findPairSum(Node *root) {

    unordered_set<int> hashTable;

    return findSumUntill(root->left, hashTable, root->data) || findSumUntill(root->right, hashTable, root->data);
}

int main()
{
    struct Node *root = newnode(7);
    root->left = newnode(2);
    root->right = newnode(3);
    root->left->left = newnode(5);
    root->left->right = newnode(9);
    root->left->left->left = newnode(1);
    root->left->left->right = newnode(6);
    root->right->left = newnode(8);

    if(findPairSum(root))
       cout<<"Pair with sum equal to root value found";
    else
       cout<<"No pairs found";
    return 0;
}

出力

Pair with sum equal to root value found

プログラムのポイント

この実装の重要な点は、再帰呼び出しから戻る際に hashTable.erase(node->data) を呼び出しているところです。これにより、ハッシュセットには常に「現在探索中のルートからリーフへの1本の経路上のノードの値」のみが格納され、別の枝のノードと誤ってペア判定されることを防いでいます。

また、探索はルート自身ではなく root->leftroot->right から開始しています。これは、ペアの候補となるノードが「ルート以外の経路上のノード」であるためです。

計算量

時間計算量: O(N) ― 各ノードを最大1回ずつ訪問し、ハッシュ操作は平均O(1)です。
空間計算量: O(H) ― ハッシュセットには経路上のノード数(木の高さH分)しか値が保持されず、再帰スタックもO(H)です。

  1. C++で解くパス合計III:DFSで二分木のルートからリーフまでの経路を探索

    整数のキーを持つノードで構成される二分木が与えられたとき、合計値が指定した値と一致する「ルートからリーフ(葉)までの経路」をすべて見つける問題を考えます。経路は必ず根から始まり、葉で終わる必要があります。 問題の例 たとえば、次のような二分木 [5,4,8,11,null,13,4,7,2,null,null,5,1] があり、目標の合計値が 22 だとします。 このとき、条件を満たす経路は次の 2 本です。 [[5, 4, 11, 2], [5, 8, 4, 5]] 解法のアプローチ:DFS(深さ優先探索)+バックトラック この問題は、少し手を加えた DFS(深さ優先探索)関数で効率的に解

  2. 【C++】二分木のルートからリーフへの全経路を相対位置付きで出力する方法

    問題の概要 この記事では、二分木が与えられたときに、ルート(根)からリーフ(葉)までのすべての経路を出力する方法を解説します。出力の際には、アンダースコア「_」を用いて各ノードの相対的な水平位置を視覚的に表現します。 まず、具体例を見ながら内容を理解していきましょう。 入力: 出力: _ _ 3 _ 9 1 _3 9 _7 3 _ 4 _ _ 2 3 9 4 1 7 6 2 3 _ 4 6 解決のアプローチ:垂直順序の活用 この問題を解く鍵となるのは、木の要素の垂直順序(vertical order)という概念です。 上図のように、ルートの水平距離を0とし、左の子へ移動するたびに-1、右の