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

C++로 복제된 이진 트리에서 원본 트리의 해당 노드 찾기

원본(original)과 복제본(cloned)이라는 두 개의 이진 트리가 있고, 원본 트리 내 특정 노드인 target에 대한 참조가 주어졌다고 가정해 봅시다. 복제된 트리는 원본 트리와 구조 및 값이 완전히 동일한 복사본입니다. 이때 우리가 해야 할 작업은 복제된 트리에서 원본의 target 노드와 동일한 위치에 있는 노드의 참조를 찾아 반환하는 것입니다.

예를 들어 아래와 같은 트리에서 target이 값 3을 가진 노드라면, 출력 결과 역시 3이 됩니다.

C++로 복제된 이진 트리에서 원본 트리의 해당 노드 찾기

문제 해결 접근 방법

두 트리의 구조가 완전히 동일하기 때문에, 원본 트리와 복제 트리를 동시에 순회하면 됩니다. 순회 중 원본 트리에서 target 노드에 도달하는 순간, 복제 트리에서의 현재 위치가 바로 찾고자 하는 노드입니다. 재귀(DFS) 방식으로 다음 단계를 따릅니다.

  • solve()라는 메서드를 정의합니다. 이 메서드는 node1(원본 트리의 현재 노드), node2(복제 트리의 현재 노드), target을 매개변수로 받습니다.
  • node1이 null이면 null을 반환합니다.
  • node1이 target과 동일하고, node1의 값이 node2의 값과 일치하면 node2를 반환합니다.
  • leftPart := solve(node1의 왼쪽 자식, node2의 왼쪽 자식, target)
  • rightPart := solve(node1의 오른쪽 자식, node2의 오른쪽 자식, target)
  • leftPart가 null이 아니면 leftPart를 반환하고, 그렇지 않으면 rightPart를 반환합니다.
  • 메인 메서드에서 solve(original, cloned, target)을 호출하여 최종 결과를 반환합니다.

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:
    TreeNode* solve(TreeNode* node1, TreeNode* node2, TreeNode*
    target){
        if(!node1) return NULL;
        if(node1 == target && node1->val == node2->val) return node2;
        TreeNode* leftPart = solve(node1->left, node2->left, target);
        TreeNode* rightPart = solve(node1->right, node2->right, target);
        return leftPart? leftPart : rightPart;
    }
    TreeNode* getTargetCopy(TreeNode* original, TreeNode* cloned,
    TreeNode* target) {
        return solve(original, cloned, target);
    }
};
main(){
    vector<int> v = {7,4,3,NULL,NULL,6,19};
    TreeNode *root = make_tree(v);
    TreeNode *cloned = make_tree(v);
    TreeNode *target = root->right; //값이 3인 노드
    Solution ob;
    cout << (ob.getTargetCopy(root, cloned, target))->val;
}

입력

[7,4,3,null,null,6,19]
3

출력

3

알고리즘 설명

위 코드는 원본 트리와 복제 트리를 루트부터 깊이 우선 탐색(DFS)으로 동시에 내려갑니다. 각 재귀 호출에서 원본 트리의 현재 노드가 target과 포인터 수준에서 일치하는지 확인하고, 일치한다면 복제 트리의 같은 위치에 있는 노드를 즉시 반환합니다. 왼쪽 서브트리에서 결과를 찾지 못하면 오른쪽 서브트리를 계속 탐색하며, 모든 노드를 한 번씩만 방문하므로 시간 복잡도는 O(N), 재귀 호출 스택에 의한 공간 복잡도 역시 최악의 경우 O(N)입니다.