자료구조

자료구조 - AVL 트리

tita 2024. 6. 11. 21:02

트리에는 균현 트리와 경사 트리가 있습니다.

경사 트리가 될수록 순차 탐색과 같은 탐색 시간을 가지게 되기 때문에 균형을 유지하는 것이 매우 중요합니다.

 

 

[AVL 트리]

AVL 트리는 높이 균형 이진 탐색 트리의 한 종류로, 각각의 노드에서 왼쪽 서브트리와 오른쪽 서브트리의 높이 차이가 1 이하가 되도록 유지합니다.

이 균형을 유지하기 위해 삽입 및 삭제 연산이 발생할 때 트리를 회전시켜 균형을 맞춥니다.

 

AVL 트리는 높이가 에 비례하도록 유지되므로, 탐색, 삽입, 삭제 연산의 시간 복잡도가 입니다.

 

 

 

AVL 트리 작동 원리

  1. 삽입:
    • 새로운 노드를 삽입한 후, 트리의 균형을 맞추기 위해 필요한 경우 회전을 수행합니다.
  2. 삭제:
    • 노드를 삭제한 후, 트리의 균형을 맞추기 위해 필요한 경우 회전을 수행합니다.
  3. 회전:
    • 단순 회전: LL (Left-Left), RR (Right-Right)
    • 이중 회전: LR (Left-Right), RL (Right-Left)

 

 

AVL 트리 의사 코드

함수 insert(node, key):
    만약 node가 null이면:
        새로운 노드를 생성하고 반환
    만약 key가 node의 값보다 작으면:
        node의 왼쪽 자식에 삽입
    그렇지 않으면:
        node의 오른쪽 자식에 삽입

    node의 높이를 갱신
    균형 인수를 계산

    만약 균형 인수가 2 이상이면:
        만약 key가 node의 왼쪽 자식의 값보다 작으면:
            node = rightRotate(node) // LL 회전
        그렇지 않으면:
            node의 왼쪽 자식에 leftRotate 수행 후, node = rightRotate(node) // LR 회전
    만약 균형 인수가 -2 이하이면:
        만약 key가 node의 오른쪽 자식의 값보다 크면:
            node = leftRotate(node) // RR 회전
        그렇지 않으면:
            node의 오른쪽 자식에 rightRotate 수행 후, node = leftRotate(node) // RL 회전

    node를 반환

 

 

 

AVL 트리 예제

예제 배열: [10, 20, 30, 40, 50, 25]

1. 10 삽입: 10
2. 20 삽입: 10, 20
3. 30 삽입: (20이 루트가 되고) 20, 10, 30 (RR 회전)
4. 40 삽입: 20, 10, 30, 40
5. 50 삽입: (30이 루트가 되고) 30, 20, 40, 10, 50 (RR 회전)
6. 25 삽입: (40이 루트가 되고) 30, 20, 40, 10, 25, 50 (LR 회전)

 

 

 

AVL 트리 코드

#include <iostream>
using namespace std;

struct Node 
{
    int key;
    Node *left, *right;
    int height;
    Node(int k) : key(k), left(nullptr), right(nullptr), height(1) {}
};

// 노드의 높이를 반환
int height(Node* node) 
{
    return node ? node->height : 0;
}

// 노드의 균형 인수를 계산
int getBalance(Node* node) 
{
    return node ? height(node->left) - height(node->right) : 0;
}

// 오른쪽 회전
Node* rightRotate(Node* y) 
{
    Node* x = y->left;
    Node* T2 = x->right;
    x->right = y;
    y->left = T2;
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
    return x;
}

// 왼쪽 회전
Node* leftRotate(Node* x) 
{
    Node* y = x->right;
    Node* T2 = y->left;
    y->left = x;
    x->right = T2;
    x->height = max(height(x->left), height(x->right)) + 1;
    y->height = max(height(y->left), height(y->right)) + 1;
    return y;
}

// 노드를 삽입
Node* insert(Node* node, int key)
{
    // 표준 BST 삽입
    if (!node) return new Node(key);
    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node;

    // 노드의 높이를 갱신
    node->height = 1 + max(height(node->left), height(node->right));

    // 균형 인수를 계산
    int balance = getBalance(node);

    // LL 회전
    if (balance > 1 && key < node->left->key)
        return rightRotate(node);

    // RR 회전
    if (balance < -1 && key > node->right->key)
        return leftRotate(node);

    // LR 회전
    if (balance > 1 && key > node->left->key) 
    {
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    // RL 회전
    if (balance < -1 && key < node->right->key) 
    {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node;
}

// 중위 순회
void inOrder(Node* root) 
{
    if (root) 
    {
        inOrder(root->left);
        cout << root->key << " ";
        inOrder(root->right);
    }
}

int main() 
{
    Node* root = nullptr;
    root = insert(root, 10);
    root = insert(root, 20);
    root = insert(root, 30);
    root = insert(root, 40);
    root = insert(root, 50);
    root = insert(root, 25);

    cout << "중위 순회 결과: ";
    inOrder(root);

    return 0;
}

 

 

AVL 트리의 장단점

 

장점 :

  1. 높이 균형 유지: 높이가 log⁡(n)\log(n)로 유지되어 삽입, 삭제, 탐색의 시간 복잡도가 log⁡(n)\log(n)입니다.
  2. 성능 보장: 최악의 경우에도 효율적인 성능을 보장합니다.

단점 :

  1. 복잡성: 구현이 상대적으로 복잡하며, 회전 연산이 추가됩니다.
  2. 추가 메모리 사용: 각 노드가 높이 정보를 저장해야 하므로 메모리 사용량이 약간 증가합니다.

 

 

시간 복잡도

 

  • 삽입, 삭제, 탐색: O(log⁡n) (균형 트리 구조 덕분에)
  • 회전 연산: 각 회전 연산의 시간 복잡도는 O(1)

 

 

 

ㅇㅅㅇ