트리에는 균현 트리와 경사 트리가 있습니다.
경사 트리가 될수록 순차 탐색과 같은 탐색 시간을 가지게 되기 때문에 균형을 유지하는 것이 매우 중요합니다.
[AVL 트리]
AVL 트리는 높이 균형 이진 탐색 트리의 한 종류로, 각각의 노드에서 왼쪽 서브트리와 오른쪽 서브트리의 높이 차이가 1 이하가 되도록 유지합니다.
이 균형을 유지하기 위해 삽입 및 삭제 연산이 발생할 때 트리를 회전시켜 균형을 맞춥니다.
AVL 트리는 높이가 에 비례하도록 유지되므로, 탐색, 삽입, 삭제 연산의 시간 복잡도가 입니다.
AVL 트리 작동 원리
- 삽입:
- 새로운 노드를 삽입한 후, 트리의 균형을 맞추기 위해 필요한 경우 회전을 수행합니다.
- 삭제:
- 노드를 삭제한 후, 트리의 균형을 맞추기 위해 필요한 경우 회전을 수행합니다.
- 회전:
- 단순 회전: 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 트리의 장단점
장점 :
- 높이 균형 유지: 높이가 log(n)\log(n)로 유지되어 삽입, 삭제, 탐색의 시간 복잡도가 log(n)\log(n)입니다.
- 성능 보장: 최악의 경우에도 효율적인 성능을 보장합니다.
단점 :
- 복잡성: 구현이 상대적으로 복잡하며, 회전 연산이 추가됩니다.
- 추가 메모리 사용: 각 노드가 높이 정보를 저장해야 하므로 메모리 사용량이 약간 증가합니다.
시간 복잡도
- 삽입, 삭제, 탐색: O(logn) (균형 트리 구조 덕분에)
- 회전 연산: 각 회전 연산의 시간 복잡도는 O(1)
ㅇㅅㅇ
'자료구조' 카테고리의 다른 글
자료구조 - 해싱 (0) | 2024.06.11 |
---|---|
자료구조 - 2-3 트리 (0) | 2024.06.11 |
자료구조 - 보간 탐색 (1) | 2024.06.11 |
자료구조 - 색인 순차 탐색 (0) | 2024.06.11 |
자료구조 - 이진 탐색 (1) | 2024.06.11 |