알고리즘(Algorithm)

이진 탐색 트리 (Binary Search Tree, BST)

임베디드 친구 2025. 1. 21. 08:55
반응형

이진 탐색 트리(BST, Binary Search Tree)는 이진 트리의 한 종류로, 모든 노드가 특정한 정렬 순서를 만족하는 자료구조입니다. 각 노드는 최대 두 개의 자식을 가지며, 특정 규칙에 따라 트리를 정렬하여 효율적인 탐색, 삽입, 삭제 작업을 가능하게 합니다. 이 글에서는 BST의 개념을 기초부터 설명하고, Java와 C를 사용하여 구현 예제를 제공합니다.

이진 탐색 트리란?

이진 탐색 트리는 다음과 같은 특성을 가집니다.

  • 각 노드에는 키 값이 저장됩니다.
  • 왼쪽 서브트리의 모든 키는 루트 노드의 키보다 작습니다.
  • 오른쪽 서브트리의 모든 키는 루트 노드의 키보다 큽니다.
  • 이 속성은 트리의 모든 서브트리에 대해 적용됩니다.

이 특성 덕분에 BST는 이진 탐색(binary search)을 트리 형태로 확장한 구조로 볼 수 있으며, 평균적인 시간 복잡도가 (O(\log n))으로 매우 효율적입니다. 그러나 편향된 형태로 생성되면 시간 복잡도가 최악의 경우 (O(n))이 될 수 있습니다.

주요 연산

BST는 탐색, 삽입, 삭제와 같은 주요 연산을 지원합니다.

  • 탐색(Search): 특정 키가 트리 내에 존재하는지 확인합니다.
  • 삽입(Insertion): 새로운 키를 적절한 위치에 삽입합니다.
  • 삭제(Deletion): 특정 키를 제거합니다. 삭제의 경우 노드의 자식 수에 따라 다르게 처리됩니다.

이 연산들은 모두 트리의 균형에 따라 성능이 달라질 수 있으며, 평균적인 경우에는 (O(\log n))의 시간 복잡도를 가집니다.

Java로 구현하기

아래는 이진 탐색 트리의 기본적인 Java 구현 예제입니다.

class Node {
    int key;
    Node left, right;

    public Node(int item) {
        key = item;
        left = right = null;
    }
}

class BinarySearchTree {
    Node root;

    BinarySearchTree() {
        root = null;
    }

    void insert(int key) {
        root = insertRec(root, key);
    }

    Node insertRec(Node root, int key) {
        if (root == null) {
            root = new Node(key);
            return root;
        }

        if (key < root.key)
            root.left = insertRec(root.left, key);
        else if (key > root.key)
            root.right = insertRec(root.right, key);

        return root;
    }

    void inorder() {
        inorderRec(root);
    }

    void inorderRec(Node root) {
        if (root != null) {
            inorderRec(root.left);
            System.out.print(root.key + " ");
            inorderRec(root.right);
        }
    }

    public static void main(String[] args) {
        BinarySearchTree tree = new BinarySearchTree();

        tree.insert(50);
        tree.insert(30);
        tree.insert(20);
        tree.insert(40);
        tree.insert(70);
        tree.insert(60);
        tree.insert(80);

        tree.inorder();
    }
}

위의 코드에서는 기본적인 삽입 연산과 중위 순회(inorder traversal)를 구현했습니다. insert 메서드는 새로운 노드를 트리에 삽입하며, inorder 메서드는 트리의 모든 노드를 중위 순서로 출력합니다.

C로 구현하기

다음은 이진 탐색 트리의 C 언어 구현 예제입니다.

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int key;
    struct Node *left, *right;
};

struct Node* newNode(int item) {
    struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
    temp->key = item;
    temp->left = temp->right = NULL;
    return temp;
}

struct Node* insert(struct Node* node, int key) {
    if (node == NULL) return newNode(key);

    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);

    return node;
}

void inorder(struct Node* root) {
    if (root != NULL) {
        inorder(root->left);
        printf("%d ", root->key);
        inorder(root->right);
    }
}

int main() {
    struct Node* root = NULL;
    root = insert(root, 50);
    insert(root, 30);
    insert(root, 20);
    insert(root, 40);
    insert(root, 70);
    insert(root, 60);
    insert(root, 80);

    inorder(root);

    return 0;
}

C 코드에서도 Java 코드와 유사하게 노드를 삽입하고 중위 순회를 통해 트리의 요소들을 출력합니다. newNode 함수는 새로운 노드를 생성하며, insert 함수는 재귀적으로 트리에 노드를 삽입합니다.

BST의 장단점

장점

  • 효율적인 탐색: 평균적으로 (O(\log n))의 시간 복잡도를 가지며, 효율적인 데이터 탐색을 지원합니다.
  • 동적 데이터 관리: BST는 동적 데이터 삽입과 삭제에 유리합니다.

단점

  • 균형 문제: 트리가 편향되면 시간 복잡도가 최악의 경우 (O(n))이 될 수 있습니다. 이를 해결하기 위해 AVL 트리나 레드-블랙 트리와 같은 자가 균형 이진 탐색 트리를 사용할 수 있습니다.

활용 사례

BST는 여러 가지 활용 사례가 있습니다. 예를 들어, 데이터베이스에서 데이터를 효율적으로 관리하거나, 정렬된 데이터를 빠르게 탐색하는 데 사용됩니다. 또한, BST는 집합(set)과 맵(map)과 같은 자료구조의 기본이 되기도 합니다.

결론

이진 탐색 트리는 효율적인 탐색, 삽입, 삭제 연산을 지원하는 중요한 자료구조입니다. 트리의 균형이 잘 유지될 경우 매우 빠른 성능을 보장하지만, 그렇지 않을 경우 최악의 성능을 가질 수 있습니다. Java와 C로 구현된 예제를 통해 BST의 기본 개념을 이해하고 직접 구현해 보세요.

BST를 배우는 것은 더 복잡한 트리 자료구조를 이해하는 데 중요한 첫걸음이 될 것입니다.

반응형