Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

Two Sum IV – C++로 이진 탐색 트리(BST)에서 두 수의 합 찾기

문제 소개

이진 탐색 트리(Binary Search Tree, BST)와 하나의 목표값(target)이 주어졌을 때, 트리 내에 존재하는 두 원소의 합이 목표값과 같아지는 경우가 있는지 확인하는 문제입니다.

예를 들어 아래와 같은 BST가 입력으로 주어지면,

Two Sum IV – C++로 이진 탐색 트리(BST)에서 두 수의 합 찾기

목표값이 9일 때 5+4=9 또는 6+3=9가 성립하므로 출력은 True가 됩니다.

해결 전략

이 문제는 크게 두 단계로 나누어 접근할 수 있습니다.

  1. 중위 순회(Inorder Traversal): BST를 중위 순회하면 값들이 오름차순으로 정렬된 배열을 얻을 수 있습니다.
  2. 투 포인터(Two Pointer): 정렬된 배열의 양 끝에서부터 포인터를 이동시키며, 합이 목표값이 되는 두 수를 효율적으로 찾습니다.

알고리즘 단계

  • 배열 v를 정의합니다.
  • 루트 노드를 매개변수로 받는 inorder() 함수를 정의합니다.
  • 루트가 null이면 그대로 return 합니다.
  • inorder(루트의 왼쪽 자식)을 재귀 호출합니다.
  • 루트의 값을 배열 v에 삽입합니다.
  • inorder(루트의 오른쪽 자식)을 재귀 호출합니다.
  • k를 매개변수로 받는 findnode() 함수를 정의합니다.
  • n := 배열 v의 크기로 설정하고, i = 0, j = n-1로 초기화합니다.
  • i < j인 동안 다음을 반복합니다.
    • t := v[i] + v[j]
    • t == k이면 true를 반환합니다.
    • t < k이면 i를 1 증가시킵니다.
    • 그 외의 경우에는 j를 1 감소시킵니다.
  • 반복문이 종료되면 false를 반환합니다.
  • 메인 메서드에서는 다음 순서로 처리합니다.
    • inorder(root)를 호출해 트리의 모든 값을 배열에 저장합니다.
    • 배열 v를 정렬합니다.
    • findnode(k)의 결과를 반환합니다.

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:
    vector<int> v;
    void inorder(TreeNode* root){
        if (root == NULL || root->val == 0)
            return;
        inorder(root->left);
        v.push_back(root->val);
        inorder(root->right);
    }
    bool findnode(int k){
        int n = v.size(), i = 0, j = n - 1;
        while (i < j) {
            int t = v[i] + v[j];
            if (t == k)
                return true;
            if (t < k)
                i++;
            else
                j--;
        }
        return false;
    }
    bool findTarget(TreeNode* root, int k){
        inorder(root);
        sort(v.begin(), v.end());
        return findnode(k);
    }
};
main(){
    Solution ob;
    vector<int> v = {5,3,6,2,4,NULL,7};
    TreeNode *root = make_tree(v);
    cout << (ob.findTarget(root, 9));
}

입력 및 출력

입력:

{5,3,6,2,4,NULL,7}, 9

출력:

1

출력값 1은 true를 의미합니다. 즉, 트리 안에 합이 9가 되는 두 노드(예: 5와 4, 또는 6과 3)가 실제로 존재한다는 뜻입니다.

시간 및 공간 복잡도

  • 시간 복잡도: O(n log n) — 중위 순회와 투 포인터 탐색은 각각 O(n)이지만, 정렬 과정에서 O(n log n)이 추가로 소요됩니다. 참고로 BST의 중위 순회 결과는 이미 오름차순으로 정렬되어 있으므로, 정렬 단계를 생략하면 O(n)까지 최적화할 수 있습니다.
  • 공간 복잡도: O(n) — 트리의 모든 값을 저장할 배열과 재귀 호출 스택이 필요합니다.