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

이진 탐색 트리(BST)의 핵심 개념과 C++ 구현 완벽 가이드

이진 탐색 트리(Binary Search Tree, BST)는 데이터를 효율적으로 저장하고 검색하기 위해 특정 규칙을 갖는 이진 트리입니다. 일반적인 이진 트리와 달리, 노드의 배치에 엄격한 순서 규칙이 적용되기 때문에 탐색·삽입·삭제 작업을 빠르게 수행할 수 있습니다.

이진 탐색 트리의 성질

이진 탐색 트리는 다음과 같은 조건을 만족해야 합니다.

  • 모든 이진 탐색 트리는 이진 트리이다.
  • 왼쪽 자식 노드는 항상 부모(루트) 노드보다 작은 값을 가진다.
  • 오른쪽 자식 노드는 항상 부모(루트) 노드보다 큰 값을 가진다.
  • 이상적인 이진 탐색 트리에서는 같은 값이 중복되어 저장되지 않는다.

중위 순회(Inorder Traversal)로 확인하기

이러한 성질을 만족하는 트리를 중위 순회(왼쪽 서브트리 → 루트 → 오른쪽 서브트리 순서)로 탐색하면, 노드들이 항상 오름차순으로 정렬된 결과를 얻게 됩니다. 예를 들어 다음과 같은 트리가 있다고 가정해 보겠습니다.

이진 탐색 트리(BST)의 핵심 개념과 C++ 구현 완벽 가이드

위 트리는 앞서 언급한 모든 성질을 만족하는 이진 탐색 트리입니다. 이 트리를 중위 순회하면 5, 8, 10, 15, 16, 20, 23이라는 정렬된 결과가 출력됩니다. 이것이 바로 이진 탐색 트리의 핵심 특징입니다.

C++로 구현하는 이진 탐색 트리

아래 코드는 노드 생성, 삽입, 중위 순회 기능을 포함한 간단한 이진 탐색 트리의 C++ 구현 예제입니다.

예제 코드

#include<iostream>
using namespace std;
class node{
    public:
        int h_left, h_right, bf, value;
        node *left, *right;
};
class tree{
    private:
        node *get_node(int key);
    public:
        node *root;
        tree(){
            root = NULL; // 처음에는 루트를 NULL로 설정
        }
        void inorder_traversal(node *r);
        node *insert_node(node *root, int key);
};
node *tree::get_node(int key){
    node *new_node;
    new_node = new node; // 새 노드를 동적으로 생성
    new_node->h_left = 0; new_node->h_right = 0;
    new_node->bf = 0;
    new_node->value = key; // 주어진 키 값을 저장
    new_node->left = NULL; new_node->right = NULL;
    return new_node;
}
void tree::inorder_traversal(node *r){
    if(r != NULL){ // 루트가 존재하면 왼쪽 - 루트 - 오른쪽 순으로 방문
        inorder_traversal(r->left);
        cout << r->value << " ";
        inorder_traversal(r->right);
    }
}
node *tree::insert_node(node *root, int key){
    if(root == NULL){
        return (get_node(key)); // 트리가 비어 있으면 새 노드를 루트로 생성
    }
    if(key < root->value){ // 키가 루트 값보다 작으면 왼쪽으로 이동
        root->left = insert_node(root->left, key);
    }else if(key > root->value){ // 키가 루트 값보다 크면 오른쪽으로 이동
        root->right = insert_node(root->right, key);
    }
    return root; // 키가 이미 존재하면 다시 삽입하지 않음
}
main(){
    node *root;
    tree my_tree;
    // 트리에 여러 키를 삽입
    my_tree.root = my_tree.insert_node(my_tree.root, 10);
    my_tree.root = my_tree.insert_node(my_tree.root, 5);
    my_tree.root = my_tree.insert_node(my_tree.root, 16);
    my_tree.root = my_tree.insert_node(my_tree.root, 20);
    my_tree.root = my_tree.insert_node(my_tree.root, 15);
    my_tree.root = my_tree.insert_node(my_tree.root, 8);
    my_tree.root = my_tree.insert_node(my_tree.root, 23);
    cout << "In-Order Traversal: ";
    my_tree.inorder_traversal(my_tree.root);
}

실행 결과

In-Order Traversal: 5 8 10 15 16 20 23

코드 동작 원리 정리

  • 노드 삽입: 삽입할 키가 현재 노드보다 작으면 왼쪽 서브트리로, 크면 오른쪽 서브트리로 재귀적으로 이동하여 적절한 위치에 배치합니다.
  • 중복 처리: 이미 존재하는 키는 삽입하지 않고 그대로 반환합니다.
  • 중위 순회: 재귀 호출을 통해 왼쪽 → 루트 → 오른쪽 순서로 방문하므로 항상 정렬된 출력을 얻습니다.

이처럼 이진 탐색 트리는 단순한 구조만으로도 데이터를 체계적으로 관리할 수 있으며, 균형 잡힌 경우 탐색 시간 복잡도는 O(log n)으로 매우 효율적입니다. 실무에서는 AVL 트리나 레드-블랙 트리 같은 자가 균형 이진 탐색 트리가 널리 활용됩니다.