이 튜토리얼에서는 이진 탐색 트리(Binary Search Tree, BST)에서 주어진 숫자와 합이 같은 쌍(pair)을 찾는 프로그램을 작성해 보겠습니다.
쌍을 효율적으로 찾기 위해 트리의 값들을 두 개의 서로 다른 리스트에 저장하는 방식을 사용합니다. 그럼 문제를 해결하는 단계를 하나씩 살펴보겠습니다.
이진 트리를 위한 구조체(struct) 노드를 생성합니다.
이진 탐색 트리에 새 노드를 삽입하는 함수를 작성합니다.
이진 탐색 트리에서는 루트보다 작은 요소들은 왼쪽에, 큰 요소들은 오른쪽에 위치한다는 규칙을 기억하세요.
트리의 왼쪽 노드와 오른쪽 노드를 각각 저장할 두 개의 빈 리스트를 초기화합니다.
왼쪽 또는 오른쪽 노드가 NULL이 되거나 두 리스트가 모두 비어 있지 않은 동안 이진 탐색 트리를 순회합니다.
모든 요소를 왼쪽 노드 리스트에 저장하는 반복문을 작성합니다.
모든 요소를 오른쪽 노드 리스트에 저장하는 반복문을 작성합니다.
각 리스트에서 마지막 노드를 가져옵니다.
두 값을 비교합니다.
왼쪽 노드의 값이 오른쪽 노드의 값보다 크거나 같으면 반복문을 종료합니다.
두 값의 합이 주어진 숫자와 같으면 결과를 출력하고 반복문을 종료합니다.
두 값의 합이 주어진 숫자보다 작으면 왼쪽 리스트에서 마지막 노드를 제거하고 해당 노드의 오른쪽 자식으로 이동합니다.
두 값의 합이 주어진 숫자보다 크면 오른쪽 리스트에서 마지막 노드를 제거하고 해당 노드의 왼쪽 자식으로 이동합니다.
이 방식은 정렬된 배열에서 사용하는 투 포인터(Two Pointer) 기법을 트리에 적용한 것으로, 중위 순회(inorder)와 역중위 순회(reverse inorder)를 동시에 진행하며 가장 작은 값과 가장 큰 값부터 차례대로 비교해 나갑니다.
예제
전체 코드를 살펴보겠습니다.
#include<bits/stdc++.h>
using namespace std;
struct Node{
int data;
Node *left, *right, *root;
Node(int data) {
this->data = data;
left = NULL;
right = NULL;
root = NULL;
}
};
Node* insertNewNode(Node *root, int data){
if (root == NULL) {
root = new Node(data);
return root;
}
if (root->data < data) {
root->right = insertNewNode(root->right, data);
}
else if (root->data > data) {
root->left = insertNewNode(root->left, data);
}
return root;
}
void findThePairs(Node *node, int target) {
vector<Node*> left_side_nodes;
vector<Node*> right_side_nodes;
Node *current_left = node;
Node *current_right = node;
while (current_left != NULL || current_right != NULL || (left_side_nodes.size() > 0 && right_side_nodes.size() > 0)) {
while (current_left != NULL) {
left_side_nodes.push_back(current_left);
current_left = current_left->left;
}
while (current_right != NULL) {
right_side_nodes.push_back(current_right);
current_right = current_right->right;
}
Node *left_side_node = left_side_nodes[left_side_nodes.size() - 1];
Node *right_side_node = right_side_nodes[right_side_nodes.size() - 1];
int left_side_value = left_side_node->data;
int right_side_value = right_side_node->data;
if (left_side_value >= right_side_value) {
break;
}
if (left_side_value + right_side_value < target) {
left_side_nodes.pop_back();
current_left = left_side_node->right;
}
else if (left_side_value + right_side_value > target) {
right_side_nodes.pop_back();
current_right = right_side_node->left;
}
else {
cout << left_side_node->data << " " << right_side_node->data << endl;
break;
}
}
}
int main() {
Node *root = NULL;
root = insertNewNode(root, 25);
root = insertNewNode(root, 20);
root = insertNewNode(root, 30);
root = insertNewNode(root, 15);
root = insertNewNode(root, 21);
root = insertNewNode(root, 19);
root = insertNewNode(root, 31);
findThePairs(root, 36);
}출력 결과
위 프로그램을 실행하면 다음과 같은 결과를 얻을 수 있습니다.
15 21
위 예제에서 목표 합계는 36이며, 트리에서 15와 21의 합이 36이므로 해당 쌍이 출력됩니다. 이 알고리즘의 시간 복잡도는 O(n)이며, 공간 복잡도는 트리의 높이 h에 비례하여 O(h)입니다.
마무리
이번 튜토리얼에서는 이진 탐색 트리의 특성을 활용해 두 개의 리스트와 투 포인터 기법으로 목표 합계를 만족하는 쌍을 찾는 방법을 알아보았습니다. 튜토리얼에 대해 궁금한 점이 있다면 댓글 섹션에 남겨주세요.