C 자료구조와 알고리즘
AVL 트리
june__Park
2021. 6. 29. 19:17
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#define TRUE 1
#define FALSE 0
typedef struct node {
int data;
struct node* left; // 왼쪽 자식노드의 주소
struct node* right; // 오른쪽 자식노드의 주소
}NODE;
// AVL 트리 함수
int height(NODE* n);
int balance_factor(NODE* tmp);
void check_balance(NODE** p, int data);
void rotate(NODE** p, int b_factor);
NODE* root = NULL;
NODE* init(int data) {
NODE* tmp = (NODE*)malloc(sizeof(NODE));
if (tmp) {
memset(tmp, NULL, sizeof(NODE));
tmp->data = data;
}
return tmp;
}
void add(int data, NODE** addr) {
NODE* tmp = *addr;
// 공석이면 노드 대입 -> 함수 종료
if (!tmp) { // tmp == NULL
*addr = init(data);
printf("%d 저장 완료! \n", data);
check_balance(&root, data); ////////////////////////
}
// data가 작을 경우 -> 왼쪽 자식으로
else if (data < tmp->data) {
add(data, &tmp->left);
}
// data가 클 경우 -> 오른쪽 자식으로
else if (data > tmp->data) {
add(data, &tmp->right);
}
// data가 같을 경우 -> 저장 X, 함수 종료
else {
printf("중복 원소는 저장할 수 없습니다. \n");
}
}
/*
height() : 노드의 최대 높이를 구한다 ( 노드부터 가장 먼 곳의 단말 노드까지의 거리 )
인자 : 높이를 구하고 싶은 노드
리턴 : 높이
*/
int height(NODE* n) {
int left_depth, right_depth;
if (n == NULL) {
return 0;
}
left_depth = height(n->left);
right_depth = height(n->right);
return (left_depth > right_depth ? left_depth : right_depth) + 1;
}
/*
balance_factor() : 밸런스 팩터를 구한다. ( 절대값이 1 이하이 경우 균형으로 판단 )
인자 : 밸런스 팩터를 구할 노드
리턴 : 밸런트 팩터
*/
int balance_factor(NODE* tmp) {
return height(tmp->left) - height(tmp->right);
}
void check_balance(NODE** p, int data) {
int b;
NODE* n = *p;
if (!n) {
return;
}
// 현재 노드가 추가된 노드와 같다면 함수 종료
if (data == n->data) {
return;
}
// 그렇지 않으면 (루트 ~ 새 노드까지의 경유 노드라면)밸런스 팩터 구함
b = balance_factor(n); // 밸런스 팩터 구하기
if (abs(b) > 1) { // 불균형 상태라면 ( b < -1 || b > 1 )
rotate(p, b); // 회전 실행
}
check_balance(data < n->data ? &n->left : &n->right, data);
}
void rotate(NODE** p, int b_factor) {
printf("회전 실행! \n");
NODE* tmp = *p;
int first = b_factor > 0; // 1 : L, 0 : R
int second = first ? (balance_factor(tmp->left) > 0) : (balance_factor(tmp->right) > 0);
// L이면 left의 밸런스팩터를 확인하고 R이면 right의 밸런스팩터를 확인한다.
// 이때 나온 결과가 1이면 L, 0이면 R
NODE* median; // 중간값
// 1) LR (first == 1 && second == 0)
if (first && !second) {
printf("LR 실행! tmp : %p \n", tmp);
median = tmp->left;
tmp->left = median->right;
tmp->left->left = median;
median->right = NULL;
second = 1; // LL 로 변경되었음을 표시 (바로 밑의 LL 회전이 실행 되도록)
}
// 2) LL (first == 1 && second == 1)
if (first && second) {
printf("LL 실행! tmp : %p \n", tmp);
median = tmp->left;
tmp->left = median->right;
median->right = tmp;
*p = median;
}
// 3) RL
if (!first && second) {
printf("RL 실행! tmp : %p \n", tmp);
median = tmp->right;
tmp->right = median->left;
tmp->right->right = median;
median->left = NULL;
second = 0;
}
// 4) RR
if (!first && !second) {
printf("RR 실행! tmp : %p \n", tmp);
median = tmp->right;
tmp->right = median->left;
median->left = tmp;
*p = median;
}
}
void preorder(NODE* tmp) { // 전위 순회
if (!tmp) {
return;
}
printf("%d, ", tmp->data); // N 노드 확인
printf("높이 : %d \n", height(tmp)); // 노드의 높이 //////////////////////
printf("밸런스 팩터 : %d \n", balance_factor(tmp)); //////////////////////
preorder(tmp->left); // L 왼쪽 이동
preorder(tmp->right); // R 오른쪽 이동
}
void inorder(NODE* tmp) { // 중위 순회
if (!tmp) {
return;
}
inorder(tmp->left);
printf("%d ", tmp->data);
inorder(tmp->right);
}
void postorder(NODE* tmp) { // 후위 순회
if (!tmp) {
return;
}
postorder(tmp->left);
postorder(tmp->right);
printf("%d ", tmp->data);
}
int search(int data) {
NODE* tmp = root;
static int cnt = 0;
while (tmp) {
printf("%d회째 확인중.. \n", ++cnt);
if (tmp->data == data) {
return TRUE;
}
if (data < tmp->data) {
tmp = tmp->left;
}
else if (data > tmp->data) {
tmp = tmp->right;
}
}
return FALSE;
}
void delete(int data, NODE** p) {
NODE* tmp = *p;
if (tmp == NULL) {
printf("삭제할 데이터가 없습니다. \n");
}
// tmp가 삭제할 노드니?
else if (tmp->data == data) {
// *p : tmp(삭제할 노드)가 저장되어있는 곳
// ( tmp가 루트라면 : root / 일반 노드라면 : 자신이 적혀있는 부모노드 측의 right 혹은 left )
// 자식 노드가 2개니?
if (tmp->left && tmp->right) {
// 후계자 노드 설정 (왼쪽 자손 노드 중 가장 큰 수)
NODE* heir = tmp->left;
while (heir->right) {
heir = heir->right;
}
// 후계자 노드의 데이터 복사
tmp->data = heir->data;
// 후계자 노드 삭제
delete(heir->data, &tmp->left);
}
// 자식 노드가 왼쪽 1개니?
else if (tmp->left) {
*p = tmp->left; // 부모와 자식을 연결
printf("%d 삭제 완료! \n", tmp->data);
free(tmp);
}
// 자식 노드가 오른쪽 1개거나, 자식이 0개니?
else {
*p = tmp->right; // 부모와 자식을 연결
printf("%d 삭제 완료! \n", tmp->data);
free(tmp);
}
}
// data가 tmp 보다 작니?
else if (data < tmp->data) {
delete(data, &tmp->left);
}
// data가 tmp 보다 크니?
else {
delete(data, &tmp->right);
}
}
void main() {
add(50, &root);
add(150, &root);
add(100, &root);
add(75, &root);
add(25, &root);
add(125, &root);
add(60, &root);
add(80, &root);
add(55, &root);
add(175, &root);
add(135, &root);
add(160, &root);
add(200, &root);
add(85, &root);
add(130, &root);
add(53, &root);
add(57, &root);
add(83, &root);
preorder(root);
printf("\n");
}