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

C++로 이진 트리의 수직 순회 노드 합 중 최댓값 구하기

문제 개요

이진 트리가 하나 주어져 있다고 가정해 봅시다. 우리가 해야 할 작업은 수직 순회(vertical order traversal)를 수행했을 때, 각 수직 라인(열)에 속한 노드 값들의 합을 계산하고, 그중 최댓값을 출력하는 것입니다.

예를 들어 아래와 같은 이진 트리가 있다고 하겠습니다.

C++로 이진 트리의 수직 순회 노드 합 중 최댓값 구하기

이 트리를 수직 순회하면 각 열의 노드 합은 다음과 같습니다.

4
2
1 + 5 + 6 = 12
3 + 8 = 11
7
9

여기서 가장 큰 값은 12입니다. 접근 방법은 생각보다 단순합니다. 먼저 수직 순회를 수행하여 각 열별 노드 값을 모은 뒤, 열별 합을 구하고 그중 최댓값을 찾으면 됩니다.

알고리즘 접근 방식

수직 순회는 일반적으로 너비 우선 탐색(BFS)수평 거리(horizontal distance) 개념을 활용해 구현합니다.

  • 루트 노드의 수평 거리는 0으로 설정합니다.
  • 어떤 노드의 왼쪽 자식은 부모보다 수평 거리가 1 작고(hd − 1), 오른쪽 자식은 1 큽니다(hd + 1).
  • 큐에는 노드 포인터와 해당 노드의 수평 거리를 함께 저장합니다.
  • map<int, vector<int>> 자료구조를 사용하면 같은 수평 거리를 가진 노드들이 자동으로 한 그룹으로 묶이며, 맵은 키를 오름차순으로 정렬하므로 왼쪽 열부터 오른쪽 열까지 순서대로 처리할 수 있습니다.

순회가 끝나면 맵의 각 키(열)에 대해 벡터에 담긴 노드 값들을 모두 더하고, 지금까지의 최댓값과 비교하여 더 큰 값으로 갱신합니다.

예제 코드

#include<iostream>
#include<map>
#include<vector>
#include<queue>
using namespace std;
class Node {
   public:
   int key;
   Node *left, *right;
};
Node* getNode(int key){
   Node* node = new Node;
   node->key = key;
   node->left = node->right = NULL;
   return node;
}
int getMaxSum(Node* root) {
   if (!root)
   return -1;
   int n = 0;
   int k_node = -1;
   map<int, vector<int> > current_map;
   int hd = 0;
   queue<pair<Node*, int> > que;
   que.push(make_pair(root, hd));
   while (!que.empty()) {
      pair<Node*, int> temp = que.front();
      que.pop();
      hd = temp.second;
      Node* node = temp.first;
      current_map[hd].push_back(node->key);
      if (node->left != NULL)
      que.push(make_pair(node->left, hd - 1));
      if (node->right != NULL)
      que.push(make_pair(node->right, hd + 1));
   }
   map<int, vector<int> >::iterator it;
   int maximum = INT_MIN;
   for (it = current_map.begin(); it != current_map.end(); it++) {
      int temp_sum = 0;
      for (int i = 0; i < it->second.size(); ++i) {
         temp_sum += it->second[i];
      }
      if(maximum < temp_sum){
         maximum = temp_sum;
      }
   }
   return maximum;
}
int main() {
   Node* root = getNode(1);
   root->left = getNode(2);
   root->right = getNode(3);
   root->left->left = getNode(4);
   root->left->right = getNode(5);
   root->right->left = getNode(6);
   root->right->right = getNode(7);
   root->right->left->right = getNode(8);
   root->right->right->right = getNode(9);
   cout << "Maximum sum of vertical nodes: " << getMaxSum(root);
}

실행 결과

Maximum sum of vertical nodes: 12

코드 설명 및 복잡도 분석

getMaxSum() 함수는 다음과 같은 흐름으로 동작합니다.

  1. 루트가 NULL인 경우 −1을 반환하여 빈 트리를 처리합니다.
  2. BFS를 위해 큐에 (노드, 수평 거리) 쌍을 넣고, 큐가 빌 때까지 반복합니다.
  3. 큐에서 꺼낸 노드의 키 값을 현재 수평 거리에 해당하는 맵의 벡터에 추가합니다.
  4. 왼쪽 자식은 hd − 1, 오른쪽 자식은 hd + 1로 설정하여 큐에 삽입합니다.
  5. 순회가 끝나면 각 열의 합을 계산해 최댓값을 구해 반환합니다.

시간 복잡도는 트리의 노드 수를 N이라 할 때 O(N log N)입니다. BFS 순회 자체는 O(N)이지만, 맵에 삽입할 때마다 정렬된 위치를 찾는 데 로그 시간이 소요되기 때문입니다. 공간 복잡도 역시 모든 노드를 맵에 저장하므로 O(N)입니다.

만약 열의 순서가 중요하지 않다면 unordered_map을 사용해 평균적으로 O(N)까지 성능을 개선할 수 있습니다.