Data Structure – AVL Tree (Insert Function)

AVL트리
모든 노드에 대해서 왼쪽 부분 트리와 오른쪽 부분 트리의 높이 차가 1 이하인 이진 탐색 트리

트리의 균형 상태: 균형 인수(왼쪽 부분 트리의 높이 -오른쪽 부분 트리의 높이)가 1,0,-1을 만족
트리의 불균형 상태: 균형 인수가 1 초과 or -1 미만

시간복잡도
한 레벨 씩 내려가며 삽입,삭제 연산 (높이 시간) ->
균형 인수(balance) 깨지는 것 발견 (상수 시간) ->
회전(Rotation) 처리 (상수 시간) -> AVL 트리의 연산은 O(logn)
// AVL 트리의 높이는 O(logn)

코딩 테스트 예시

size_field: [1:1], [2:2], [3:4], [4:1], [5:12], [6:2], [7:1], [8:7], [9:1], [10:4], [11:2], [12:1]
// [키 값: 서브 트리의 노드 개수]

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

typedef struct AVLNode {
    int key;
    int size_field;
    int height; 
    int balance;
    struct AVLNode* left;
    struct AVLNode* right;
}AVLNode;
typedef AVLNode* AVLNode_ptr;

// MAX 함수
int MAX(int a, int b) {
    return a > b ? a : b;
}

// 트리의 높이 구하기 
int get_height(AVLNode_ptr T) {
    if (T) {
        // 트리의 높이 = MAX(오른쪽 서브트리의 높이, 왼쪽 서브트리의 높이) +1
        int tmp = MAX(get_height(T->left), get_height(T->right));
        return 1 + tmp;
    }
    else
        return 0;
}

// 노드의 균형인수(왼쪽 서브트리의 높이 - 오른쪽 서브트리의 높이) 구하기
int get_balance(AVLNode_ptr T) {
    if (T)
        return get_height(T->left) - get_height(T->right);
}

// 노드 생성 함수
AVLNode_ptr create_node(int key) {
    AVLNode_ptr node = (AVLNode_ptr)malloc(sizeof(AVLNode));
    node->key = key;
    node->size_field = node->balance = node->height = 0; // update 함수에서 일괄 처리함 
    node->left = node->right = NULL;
    return node;
}

// 오른쪽으로 회전시키는 함수 (왼쪽 -> 오른쪽)
// LL과 LR,RL에 쓰임 
AVLNode_ptr rotate_right(AVLNode_ptr parent) {
    AVLNode_ptr child = parent->left;
    parent->left = child->right; // child의 서브 트리 한 쪽을 parent가 가져감 
    child->right = parent; // 오른쪽 회전 

    return child;
}

// 왼쪽으로 회전시키는 함수 (오른쪽 -> 왼쪽)
// RR과 LR,RL에 쓰임
AVLNode_ptr rotate_left(AVLNode_ptr parent) {
    AVLNode_ptr child = parent->right;
    parent->right = child->left; // child의 서브 트리 한 쪽을 parent가 가져감 
    child->left = parent; // 왼쪽 회전 

    return child;
}

// AVL 트리 삽입
AVLNode_ptr insert_node(AVLNode_ptr T, int key) {
    // 이진 탐색 트리의 노드 추가 수행
    if (T == NULL)
        return create_node(key);

    if (key < T->key)
        T->left = insert_node(T->left, key);

    else if (key > T->key)
        T->right = insert_node(T->right, key);

    else // 동일한 키는 허용하지 않음
        return T;

    // 노드의 균형인수 계산
    int balance = get_balance(T);

    /*
    * 회전 (삽입 시 불균형이 발생하면 진행)

    - X: 불균형이 탐지된 가장 가까운 조상 노드

    - LL 타입 처리
    X 기준으로 (왼쪽 -> 오른쪽 회전)

    - RR 타입 처리
    X 기준으로 (오른쪽 -> 왼쪽 회전)

    - LR 타입 처리
    X의 왼쪽 자식 기준으로(오른쪽 -> 왼쪽 회전) -> X 기준으로 (왼쪽 -> 오른쪽 회전)

    - RL 타입 처리
    X의 오른쪽 자식 기준으로 (왼쪽 -> 오른쪽 회전) -> X 기준으로 (오른쪽 -> 왼쪽 회전)
    */

    // LL 타입 처리
    if (balance > 1 && key < T->left->key)
        return rotate_right(T);

    // RR 타입 처리
    if (balance < -1 && key > T->right->key)
        return rotate_left(T);

    // LR 타입 처리
    if (balance > 1 && key > T->left->key) {
        T->left = rotate_left(T->left);
        return rotate_right(T);
    }

    // RL 타입 처리
    if (balance < -1 && key < T->right->key) {
        T->right = rotate_right(T->right);
        return rotate_left(T);
    }

    return T;
}

// 트리에 size_field(부분트리에 속한 노드의 개수), balence(균형인수) 부여하기 
// postorder 응용해서 제작함 
void update(AVLNode_ptr T) {
    if (T) {
        update(T->left);
        update(T->right);

        // 잎 노드
        if ((T->left == NULL) && (T->right == NULL)) { 
            T->size_field = 1;
            T->height = 1;
            T->balance = 0;
        }

        // 내부 노드 
        else { 
            if ((T->left != NULL) && (T->right != NULL)) {
                T->size_field = 1 + (T->left->size_field) + (T->right->size_field);
                T->height = 1 + MAX(T->left->height, T->right->height);
                T->balance = (T->left->height) - (T->right->height);
            }
            else if ((T->left != NULL) && (T->right == NULL)) {
                T->size_field = 1 + (T->left->size_field);
                T->height = 1 + (T->left->height);
                T->balance = (T->left->height) - 0;
            }
            else {
                T->size_field = 1 + (T->right->size_field);
                T->height = 1 + (T->right->height);
                T->balance = 0 - (T->right->height);
            }
        }

    }
}

// 전위 순회 함수 
void preorder(AVLNode_ptr T) {
    if (T) {
        printf("[%d:%d,%d] ", T->key, T->size_field, T->balance);
        preorder(T->left);
        preorder(T->right);
    }
}

int main(void) {
    AVLNode_ptr root = NULL;

    root = insert_node(root, 8);
    root = insert_node(root, 9);
    root = insert_node(root, 10);
    root = insert_node(root, 2);
    root = insert_node(root, 1);
    root = insert_node(root, 5);
    root = insert_node(root, 3);
    root = insert_node(root, 6);
    root = insert_node(root, 4);
    root = insert_node(root, 7);
    root = insert_node(root, 11);
    root = insert_node(root, 12);

    update(root); // 각 노드에 서브트리의 노드 개수와 균형인수 부여하기 

    printf("■ 전위 순회로 AVL 트리 구조 파악 (키:서브트리의 노드 개수, 균형인수)\n\n");
    preorder(root);
    printf("\n");

    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *