AVL Tree
บทความชุดนี้จะมี 4 เรื่องนั่นคือ
• Binary Search Tree หรือ BST
• AVL Tree เป็น Balanced BST พื้นฐานที่ต้องรู้
• Red Black Tree เป็น Balanced BST ที่ insert ได้รวดเร็ว
• Splay Tree เป็น Self-Adjusting BST ที่ทำงานได้ดี
AVL Tree เป็น Binary Search Tree ที่ปรับความสูงได้เอง
ถ้าความสูงด้านซ้ายกับด้านขวาต่างกันมากเกินไป
การปรับความสูงจะใช้การหมุนไปทางซ้าย หรือ หมุนทางขวา
การหมุนไปทางซ้ายตามตัวอย่างในภาพข้างข้างคือหมุน r
เริ่มต้นต้องเก็บลูกทางขวาของ r ไว้ก่อน นั่นคือ u แล้วสลับให้ลูกทางขวาของ r
เป็น ลูกทางซ้ายของ u และเปลี่ยนลูกทางซ้ายของ u ให้เป็น r
และสุดท้ายคือส่ง u กลับไป
/* r u
/ \ / \
p u => r t
/ \ / \
s t p s
*/
struct node *rotate_left(struct node *r) {
struct node *u = r->right;
r->right = u->left;
u->left = r;
return u;
}
ส่วนการหมุนไปทางขวาก็ตรงกันข้าม ในตัวอย่างจะหมุน r
เก็บค่าลูกทางซ้ายไว้ก่อนเพราะต้องหมุนขึ้นมาแทน
เก็บใส่ตัวแปร o ไว้ จากนั้นเปลี่ยนค่าลูกทางซ้ายของ r
ให้เป็นลูกทางขวาของ o และเปลี่ยนลูกทางขวาของ o ให้เป็น r
สุดท้ายก็เหมือนเดิมคือส่ง o กลับไป
/* r o
/ \ / \
o s => n r
/ \ / \
n p p s
*/
struct node *rotate_right(struct node *r) {
struct node *o = r->left;
r->left = o->right;
o->right = r;
return o;
}
เมื่อใส่ข้อมูลเข้าไปแล้วจะต้องหาค่า balance ด้วย
นั่นคือตรวจสอบความสูงฝั่งขวาและซ้ายมาหักลบกัน ถ้าต่างกันตั้งแต่ 2 ขึ้นไป
ต้องปรับความสูงใหม่
int balance = avl_height(r->right) - avl_height(r->left);
การปรับความสูงมี 4 แบบ ตามภาพคือ left-left, left-right, right-left และ right-right
n n n n
/ / \ \
l l r r
/ \ / \
j m p t
left-left left-right right-left right-right
เริ่มจากกรณีแรกคือ left-left ต้องหมุนไปทางขวานั่นเอง
n l
/ / \
l rotate_right(n) j n
/
j
left-left balanced
ในกรณีที่ 2 หรือ left-right ก็เปลี่ยนให้เป็นแบบแรกหรือ left-left ก่อน ด้วยการหมุนลูกทางซ้ายไปทางซ้าย
n n m
/ / / \
l rotate_left(l) m rotate_right(n) l n
\ /
m l
left-right left-left balanced
ตัวอย่าง code สำหรับกรณี left-left และ left-right
if (balance <= -2) {
int bl = avl_height(r->left->right) -
avl_height(r->left->left);
if (bl <= 0) { /* left-left */
return rotate_right(r);
} else { /* left-right */
r->left = rotate_left(r->left);
return rotate_right(r);
}
}
ในกรณีที่ 3 คือ right-left ก็แปลงเป็นแบบ right-right ก่อน ด้วยการหมุนลูกทางขวาไปทางขวา
n n p
\ \ / \
r rotate_right(r) p rotate_left(n) n r
/ \
p r
right-left right-right balanced
กรณีสุดท้ายคือ right-right ก็หมุนไปทางซ้ายนั่นเอง
n r
\ / \
r rotate_left(n) n t
\
t
right-right balanced
ตัวอย่าง code สำหรับกรณี right-left และ right-right
if (balance >= 2) {
int br = avl_height(r->right->right) -
avl_height(r->right->left);
if (br >= 0) { /* right-right */
return rotate_left(r);
} else { /* right-left */
r->right = rotate_right(r->right);
return rotate_left(r);
}
}
การลบข้อมูลก็ทำเหมือน Binary Search Tree แต่อย่าลืมปรับความสูงให้ใกล้เคียงกัน
#include
#include
#include
struct node {
int key;
struct node *left, *right;
};
/*
r
/ \
p u
/ \
s t
*/
struct node *rotate_left(struct node *r) {
struct node *u = r->right;
r->right = u->left;
u->left = r;
return u;
}
/*
r
/ \
o s
/ \
n p
*/
struct node *rotate_right(struct node *r) {
struct node *o = r->left;
r->left = o->right;
o->right = r;
return o;
}
int avl_max(int a, int b) {
return a > b ? a : b;
}
int avl_height(struct node *r) {
if (r == NULL) {
return -1;
} else {
return 1 + avl_max(avl_height(r->left), avl_height(r->right));
}
}
struct node *avl_adjust(struct node *r) {
int balance = avl_height(r->right) - avl_height(r->left);
if (balance <= -2) {
int bl = avl_height(r->left->right) -
avl_height(r->left->left);
if (bl <= 0) { /* left-left */
return rotate_right(r);
} else { /* left-right */
r->left = rotate_left(r->left);
return rotate_right(r);
}
}
if (balance >= 2) {
int br = avl_height(r->right->right) -
avl_height(r->right->left);
if (br >= 0) { /* right-right */
return rotate_left(r);
} else { /* right-left */
r->right = rotate_right(r->right);
return rotate_left(r);
}
}
return r;
}
struct node *avl_insert(struct node *r, int key) {
if (r == NULL) {
r = malloc(sizeof(struct node));
r->key = key;
r->left = NULL;
r->right = NULL;
return r;
} else {
if (key < r->key) {
r->left = avl_insert(r->left, key);
} else if (r->key < key) {
r->right = avl_insert(r->right, key);
}
return avl_adjust(r);
}
}
int avl_minimum(struct node *r) {
if (r == NULL) return INT_MAX;
if (r->left == NULL) return r->key;
return avl_minimum(r->left);
}
int avl_maximum(struct node *r) {
if (r == NULL) return -INT_MIN;
if (r->right == NULL) return r->key;
return avl_maximum(r->right);
}
struct node *avl_delete(struct node *r, int key) {
if (r == NULL) { return NULL; }
/* case 1: no child */
if (r->key == key && r->left == NULL && r->right == NULL) {
free(r);
return NULL;
}
/* case 2: only right child */
if (r->key == key && r->left == NULL) {
free(r);
return r->right;
}
/* case 2: only left child */
if (r->key == key && r->right == NULL) {
free(r);
return r->left;
}
/* case 3: both children */
if (r->key == key && r->left != NULL && r->right != NULL) {
int min = avl_minimum(r->right);
r->key = min;
r->right = avl_delete(r->right, min);
}
if (r->key > key) {
r->left = avl_delete(r->left, key);
}
if (r->key < key) {
r->right = avl_delete(r->right, key);
}
return avl_adjust(r);
}
void print(struct node *r) {
if (r == NULL) {
printf("( )");
return;
}
if (r->left == NULL && r->right == NULL) {
printf("(%d)", r->key);
return;
}
printf("(");
print(r->left );
printf("(%d)", r->key);
print(r->right);
printf(")");
}
int main(void) {
struct node *root = NULL;
for (int i = 0; i < 7; i++) {
root = avl_insert(root, i);
print(root);
printf("\n");
}
root = avl_delete(root, 0);
print(root);
printf("\n");
root = avl_delete(root, 1);
print(root);
printf("\n");
root = avl_delete(root, 2);
print(root);
printf("\n");
return 0;
}