bst - feusci.feu.ac.th/faa/dsa/bookpdfs/chap7-selfbalancedbst.pdfself-balanced binary search trees...
TRANSCRIPT
Self-Balanced Binary Search Tree เปน BST ชนดหนงทมการปรบความสงของ tree ใหมความสงนอยทสดเพอใหการเขาหาขอมลเปนไปอยางงายและรวดเรวขน และความสงดงกลาวจะเปนความสงทเกดข นจากความสมพนธกบจ านวนของ node ทมอยใน BST ซงการปรบความสง (หรออาจเรยกวา ความสมดล) นจะกระท าโดยอตโนมตหลงจากการเขาหาขอมล หลงจากจบบทเรยนนแลว ผอานจะไดทราบถง o โครงสรางและขอก าหนดของ AVL tree o การน าขอมลเขา (หรอออกจาก) AVL tree และการปรบความสง o โครงสรางและขอก าหนดของ Red-Black tree o การน าขอมลเขา (หรอออกจาก) Red-Black tree และการปรบความสง o โครงสรางและขอก าหนดของ Splay tree o การน าขอมลเขา (หรอออกจาก) Splay tree และการปรบความสง 7.1 AVL Tree การน าขอมลเขา การลบขอมล และการคนหาขอมลใน BST จะใชเวลานอยมาก (มประสทธภาพสง หรอทเรยกวาการใชเวลาแบบ sub-linear running time) ถา node ตาง ๆ ทอยใน tree อยในสภาพทกระจายออกไปจาก root (ทงสองขาง) ซงลกษณะดงกลาวท าใหการคนหา node ใน BST นนไวขน ผอานอยาลมวาปจจยของการคนหา node ใน tree นนสวนหนงกคอความสงของ tree ยงมความสงมากการคนหากใชเวลามาก
ภาพท 7.1 ความสงทไมเหมาะสมกบจ านวน node ทมอยใน BST (ตวเลขขาง node หมายถงระดบความสง)
34
24
14 45
76
89
82 18 92
99
0
1
2
3
4
0
1
2
0
0
8
12
14
18
20
1
2
3
4
0
Self-Balanced Binary Search Trees บทท 7
178
ภาพท 7.2 ความสงทเหมาะสมกบจ านวน node ทมอยใน BST
การทจะท าให BST ใชเวลาในการประมวลผลในระดบดนน เราตองท าใหการประมวลผลใชเวลาเทากบ log2 n (โดยท n คอจ านวน node ใน tree) ซงสามารถท าไดดวยการก าหนดให ความสมพนธของ node และความสงอยในรปของคาทใกลเคยงกบ log2 n ซงท าไดดวยการใช
คา floor ของ log2 n และคา floor ของเลขใด ๆ (x) กคอ integer ทใหญทสดทนอยกวา x เชน คา floor ของ 7.345 กคอ 7 คา floor ของ 3.1415926 กคอ 3 หรอพดงายๆ กคอ คา floor ของ integer ทเปนบวกกคอคาทถกตดทศนยมออก (truncating) จากภาพของ BST ทงสามตวในภาพท 7.1 และ 7.2 นน BST ในภาพท 7.2 เปน tree ทมความสงทดทสดเมอเปรยบเทยบอตราสวนของจ านวนของ node กบความสง ในทางคณตศาสตรเราร วา logab = y สามารถเขยนไดอกแบบคอ ay = b เพราะฉะนน BST ทางซายของภาพท 7.1 ม node = 10 ความสงเทากบ 4 นนไมใช tree ทเหมาะสมเพราะ log2 10 = 3.3219 ซงเมอหาคา floor แลวเราได 3 แตความสงจรงของ tree เทากบ 4 สวน tree ทางดานขวาของภาพเดยวกน log2 5 ~ 2 เปนความสงทเราตองการแตความสงจรงเปน 4 ส าหรบ tree ในภาพท 7.2 นนเปน tree ทเหมาะสมทงนเพราะ log2 8 = 3 (23 = 8) และความสงของ tree กเทากบสาม ดงนนถาเราตองการใหการเขาหาขอมลไวขนเราตองหาวธการทจะท าใหอตราสวนของจ านวนของ node และความสงใน tree มความสมดลกน (คาใกลเคยงกบ log2 n) วธการทจะใหไดมาซงความสมดลของ BST มหลายวธ วธการแรกทเราจะมาดกนคอวธการของ AVL tree AVL Tree ตงชอตามผสราง คอ Adelson-Velskii, และ Landis ในป ค.ศ. 1962 เปน BST ทปรบความสมดลดวยการก าหนดความสงของ sub-tree ทงสองขางใหมความแตกตางกนไมเกน 1 ความสง (ดภาพท 7.3)
34
24
14 45
76
89
37
0
2
3
0
0
1
1
บทท 7 Self-Balanced Binary Search Trees
179
ภาพท 7.3 AVL tree ทมความสมดล1
tree ทม node 23 เปน root มความสงของ tree ทางดานซาย (HL = height of left) มคาเทากบ 3 ความสงของ tree ทางดานขวา (HR = height of right) มคาเทากบ 2 ท าใหคาความแตกตางของความสงมคาเทากบ 1 ซงเปนคาทท าให tree ของเรานมการ balance ของ node สวน sub-tree ทม node 18 เปน root มความสงทางดานซายเทากบ 2 และ ความสงทางดานขวามคาเทากบ 1 ท าใหคาความแตกตางมคาเปน 1 ซงท าใหเกดความ balance ของ node
สวน sub-tree ทม node 44 เปน root มความสงทางดานซายเทากบ 0 และ ความสงทางดานขวาเทากบ 1 ท าใหคาความแตกตางมคาเปน 1 ซงท าใหเกดความ balance ของ node
เพราะฉะนนเราสามารถสรปไดวา ถา 1 HRHL เรากจะม tree ทเรยกวา AVL tree หรอ
พดงาย ๆ วา ถา tree มคาความสงของแตละระดบไมเกน 1 เราเรยก tree นวา AVL tree การทจะท าให tree มความ balance นนเราจ าเปนทจะตองวางขอก าหนดของการน า node เขา
ส tree ดวยการตรวจสอบวา node ทจะน าเขาส tree นนท าให tree เกดความสมดลหรอไม ถาไม เรากตองท าการปรบความสง ซงสามารถท าไดดวยการหมน (rotate) node ไปทางดานซาย หรอ ทางขวา โดยทวไป algorithm ตาง ๆ ท นกวทยาศาสตรทางดาน computer ไดคดคนข นมา มกจะปลอยใหการน าขอมลเขาเปนไปตามธรรมชาตของ การน าขอมลเขาของ BST แตทแตกตางออกไปกคอ จะคอยตรวจสอบถงความ balance ของ tree ทเกดข นหลงจากการน าขอมลเขาเสมอ และจะแกไขความไม balance ของ tree หรอ sub-tree นน ๆ ทนท
1 จะเรยกวาการ balance ของ node สลบกบการเรยกวาความสมดล
44
23
18
12 20
8 14
52
HL = 3 HR = 2
Self-Balanced Binary Search Trees บทท 7
180
7.1.1 ความไมสมดลของ node และ การหมน node เพอใหเกดความสมดล ในการน า node เขาส (insert) หรอดงออก (delete) ของ AVL tree นนอาจท าใหโครงสรางของ AVL tree ผดไปจากขอก าหนด (ความสงตางกนมากกวาหนง) ซงกระบวนการทท าใหเกดความไมสมดลนมอย 4 ลกษณะดงน กรณท 1: การ insert ทางดานซายของ node ทอยทางดานซายของ tree (left of left) [น าขอมลเขาส sub-tree ทางซายของลกทอยทางซายของ Node X]
ภาพท 7.4 ตองการ insert node 4 เขาส tree ภาพท 7.5 tree ไม balance หลงจากการ insert
กรณท 2: การ insert ทางดานขวาของ node ทอยทางดานขวาของ tree (right of right)
[น าขอมลเขาส sub-tree ทางขวาของลกทอยทางขวาของ Node X]
ภาพท 7.6 ตองการ insert node 44 เขาส tree ภาพท 7.7 tree ไม balance หลงจากการ insert
Node X
ลกทางซายของ Node X
18
12 20
8 14
4
เกดความไม balance ท node
18
18
12 20
8 14
4
น า node 4 เขาทางดานซายของลกของ
Node X
น า node 14 เขาทางดานขวาของลกของ node X
ลกทางขวาของ Node X
Node X 14
12 20
18 23
44
เกดความไม balance ท node
14
14
12 20
18 23
44
บทท 7 Self-Balanced Binary Search Trees
181
กรณท 3: การ insert ทางดานขวาของ node ทอยทางดานซายของ tree (right of left) [น าขอมลเขาส sub-tree ทางขวาของลกทอยทางซายของ Node X]
ภาพท 7.8 ตองการ insert node 13 ภาพท 7.9 tree ไม balance หลงจากการ insert
กรณท 4:
การ insert ทางดานซายของ node ทอยทางดานขวาของ tree (left of right) [น าขอมลเขาส sub-tree ทางซายของลกทอยทางขวาของ Node X]
ภาพท 7.10 ตองการ insert node 19 ภาพท 7.11 tree ไม balance หลงจากการ insert
เรารแลววาเมอเกดความไม balance ใน tree เราจะตองท าการหมน node ตาง ๆ เพอใหไดมาซงความ balance นน การหมนนนเราจะหมนในลกษณะของ การหมนครงเดยว หรอ หมนสองครง ทงนข นอยกบลกษณะของ tree วามความซบซอนเพยงใด โดยทวไปการหมนสองครงจะเปนการหมนไปในทศทางตรงกนขามกน คออาจเปนการหมนซายหนงครงแลวกหมนขวา หรอหมนขวาหนงครงแลวจงหมนซาย ลองมาดการหมนแบบตาง ๆ กน
ลกทางขวาของลกของ Node X
ลกของ Node X
Node X เกดความไม balance ท node
18
18
12 20
8 14
13
18
12 20
8 14
13
node 13 จะตองถกน าเขาทางดานขวาของ node 12
ลกทางขวาของ
Node X
Node X เกดความไม
balance ท node 14
14
12 20
44 18
19
14
12 20
44 18
19
node 19 ตองถกน าเขาทางดานซายของ Node 20
Self-Balanced Binary Search Trees บทท 7
182
การแกไขของกรณท 1 (และ 2): หมนขวา (rotate right) เมอเกดการไม balance ทางดานซายของ (left of left) tree
ภาพท 7.12 หลงจาก insert 12 – tree ไม balance ภาพท 7.13 หลงจากการหมนขวา
ภาพท 7.14 หลงจาก insert 4 – tree ไม balance ภาพท 7.15 หลงจากการหมนขวา
ในการหมนซาย (กรณท 2) ครงเดยวนนกท าคลาย ๆ กบ การหมนขวาทแสดงใหเหนจากภาพดานบนน ตางกนเพยงแคทศทางของการหมน (Symmetric rotation)
A
A
tmp
18
12 20
20
18
12
X
X
tmp
B
C
X tmp
B C
A
A
18
12 20
14 8
4
12
8 18
14 4 20
tmp X
บทท 7 Self-Balanced Binary Search Trees
183
การแกไขของกรณท 3 (และ 4): หมนซาย 1 คร งหลงจากน นกหมนขวาอก 1 คร ง (right of left)
ภาพท 7.16 การหมนสองครง (double rotate right) อยางงาย
ภาพท 7.17 tree ทไม balance ท าใหเราตองหมนซาย node 12
12
4
8
12
8
4
8
4 12
node 8 ท าให tree ไม balance
หลงจากหมน node 4
หลงจากหมน node 12
23
18 44
12 20
4
52
14
16
node 16 ท าให tree ไม
balance เราตองหมน node 12 ไปทางซาย 1 ครง
Self-Balanced Binary Search Trees บทท 7
184
ภาพท 7.18 การหมนครงทสอง (ของ node 18)
ภาพท 7.19 tree หลงจากการหมนครงทสอง ซงท าให tree มความ balance
ในการหมนเพอแกไขความไม balance ของกรณท 4 กเชนเดยวกนกบกรณท 3 ตางกนททศทางของการหมนเทานนเอง
23
18 44
14 20
12
52
16
4
Subtree ทเราได หลงจากการหมนซาย
ของ node 12 กอนหนาน
Tree ยงไม balance เราตองหมน node 18 ไปทางขวา
อก 1 ครง
23
14 44
12 18
4
52
16 20
บทท 7 Self-Balanced Binary Search Trees
185
7.1.2 การน า node เขาส AVL tree ในการน าขอมลเขาส AVL tree นนเราเรมดวยการตรวจสอบวา tree มขอมลหรอไม ถามเรากหาต าแหนงทเหมาะสมในการใส node ดวยการเปรยบเทยบวา ขอมลทจะน าเขามากหรอนอยกวาขอมลทอยใน node นน ถานอยกวาเรากไปทางซาย ถามากกวาเรากไปทางขวา และเมอเรามาถงต าแหนงทเหมาะสม เรากท าการน าขอมลเขาพรอมทงตรวจสอบความสมดลของ tree ถาไมสมดลเรากท าการหมน node ตามทศทางของการน าเขา ซงถาเราน าขอมลเขาทางดานซาย tree ทางซายนอาจสงเกนไป เรากท าการหมน node เพอสรางความสมดลใหกบ tree น (และเรากจะท าในลกษณะเดยวกน ถาการน าเขานนเกดทางดานขวา) ทงนการน าขอมลเขาส tree ของเรานน เราจะไมยอมใหมการน าขอมลทซ ากนเขาส tree เปนอนขาด การเขยน code ของการ insert ขอมลเขาส AVL tree นน เราจะใชเทคนคของ recursion เขามาชวยเหมอนกบทเราท ากบการ insert ของ BST แตจะเพมการตรวจสอบความไม balance ของ tree หลงจากการ insert ทนท //insert into AVL tree
public void insert(T key) {
root = insert(key, root);
}
//internal method to insert key into AVL tree
private AVLNode<T> insert(T key, AVLNode<T> node) {
//tree is empty, just insert
if(node == null) {
node = new AVLNode<T>(key);
}
//key is less than item, so we go left
else if(key.compareTo(node.item) < 0) {
node.left = insert(key, node.left);
if(node.left != null) {
//height is not balance,perform rotation
if((height(node.left) - height(node.right)) == 2) {
if(key.compareTo(node.left.item) < 0)
node = singleRotateRight(node);
else
node = doubleRotateRight(node);
}
}
}
//key is greater than item, so we go right
else if(key.compareTo(node.item) > 0) {
node.right = insert(key, node.right);
if(node.right != null) {
//height is not balance, perform rotation
if((height(node.right) - height(node.left)) == 2) {
if(key.compareTo(node.right.item) > 0)
node = singleRotateLeft(node);
else
node = doubleRotateLeft(node);
}
}
}
else ; //duplicates not allow!
return node;
}
การน า node ใหมเขาส AVL tree นนมขนตอนหลก ๆ อยสองขนตอนคอ 1) น า node เขาส tree ดวยเงอนไขและขอก าหนดของ BST นนคอ node ใหมจะถกน าเขาท leaf node ในต าแหนงทเหมาะสม และเมอ node ใหมถกน าเขาแลว คณสมบตของ AVL tree อาจผดไป ณ ต าแหนงใดต าแหนงหนงจากเสนทางของ root ไปยงต าแหนงใหมของ node ทถกน าเขา 2) เมอเงอนไขของ AVL tree ผดไปเราตองท าการปรบปรง tree ดวยการยอนกลบขนไปยง root
จาก node ใหมทน าเขาพรอมกบตรวจสอบความผดปกตดงกลาว ถาความสงของ sub-tree ทางซายและขวาใด ๆ มความสงเกนหนง เรากท าการหมน node ตาง ๆ เพอปรบปรง tree ให เปนไปตามเงอนไขทไดก าหนดไว
Self-Balanced Binary Search Trees บทท 7
186
สมมตวาเราตองน า node ใหมเขาทางดานขวา code ทเราตองเรยกกคอ node.right = insert(key, node.right); ซงในทสด (ไปจนสดทางแลว – กลบออกมาจากการเรยกของ recursion) เราจะมาอยท leaf node ของ tree ทางขวา ณ ทนเราจะท าการตรวจสอบความสงของ sub-tree ทงสองขางวาผดเงอนไขหรอไม ถาผดเราจะท าการหมน node ซงอาจเปนการหมนไปทางซายหนงครง หรออาจเปนการหมนสองครง ทงนกข นอยกบผลลพธของการเปรยบเทยบทเกดข น
การปรบดวยการหมนเพอท าใหเกดความสมดลของกรณตาง ๆ ทไดกลาวไว (1 – 4) ม method ส าหรบชวยดงน //LEFT-OF-LEFT
//insert in the left subtree of the left child of X
//do a single rotation with left child (rotate right)
//e.g. insert the followings 20, 18, and 12
private AVLNode<T> singleRotateRight(AVLNode<T> X) {
AVLNode<T> tmp = new AVLNode<T>();
tmp = X.left;
X.left = tmp.right;
tmp.right = X;
return tmp;
}
//RIGHT-OF-RIGHT
//insert in the right subtree of the right child of X
//do a single rotation with right child (rotate left)
//e.g. insert the followings 12, 18, and 20
private AVLNode<T> singleRotateLeft(AVLNode<T> X) {
AVLNode<T> tmp = new AVLNode<T>();
tmp = X.right;
X.right = tmp.left;
tmp.left = X;
return tmp;
}
//RIGHT-OF-LEFT
//insert in the right subtree of the left child of X
//do a single rotation on left child, then rotate right on X
//e.g. insert the followings 12, 4, and 8
private AVLNode<T> doubleRotateRight(AVLNode<T> X) {
X.left = singleRotateLeft(X.left);
return singleRotateRight(X);
}
//LEFT-OF-RIGHT
//insert in the left subtree of the right child of X
//do a single rotation on right child, then rotate left on X
//e.g. insert the followings 12, 44, and 18
private AVLNode<T> doubleRotateLeft(AVLNode<T> X) {
X.right = singleRotateRight(X.right);
return singleRotateLeft(X);
}
7.1.3 การลบ node ออกจาก AVL tree
ส าหรบการลบ node ออกจาก AVL tree นนกท าคลาย ๆ กบการลบ node ออกจาก BST เพยงแตวาในการลบนนเราจะตองตรวจสอบความสมดลของ tree วายงมอยหรอไม ซงวธการกคลายกบการน าขอมลเขา เราจะปรบความสมดลหลงจากทเราลบ node ออกจาก tree เพอใหการลบ node มเสถยรภาพ (stable – ลบแลวโครงสรางใหมยงถกตองตามขอก าหนดของ AVL tree) เราจงก าหนดใหมตวแปรสองตว lastNode และ deleteNode เปนตวท าหนาทในการจ า node ทมการเขาถงระหวางการคนหา node ทตองการลบโดยท lastNode จะเปนตว
เกบ node ทเราไดท าการเขาหาครงสดทาย และ deleteNode จะเปนตวเกบ node ทเราตองการจะลบ //delete node with given key from AVL tree
//replace deleted node with the minimum
บทท 7 Self-Balanced Binary Search Trees
187
//node on its right subtree
public void delete(T key) {
lastNode = null;
deleteNode = null;
root = delete(key, root);
}
//internal method to delete node with given key
//uses inorder successor as replacement node
private AVLNode<T> delete(T key, AVLNode<T> node) {
//tree is empty, cannot delete
if(node == null) {
return null;
}
lastNode = node; //last node seen
//key to delete is on the left
if(key.compareTo(node.item) < 0)
node.left = delete(key, node.left);
//key is on the right
else {
deleteNode = node; //potential node to delete
node.right = delete(key, node.right);
}
//returning from the calls
if(node == lastNode) {
//make sure its our target node
if(deleteNode != null && key.equals(deleteNode.item)) {
//node is rightmost in its subtree
if(deleteNode == lastNode) {
//return left subtree
node = node.left;
}
//node is not rightmost, replace deleted node
//with minimum node on the right subtree
else {
//swap data
T tmp = deleteNode.item;
deleteNode.item = lastNode.item;
lastNode.item = tmp;
//return right subtree
node = node.right;
}
}
}
//adjust the nodes
else {
//left tree is taller than right tree
if((height(node.left) - height(node.right)) == 2) {
if(key.compareTo(node.left.item) < 0)
node = singleRotateRight(node);
else
node = doubleRotateRight(node);
}
//right tree is taller than left tree
if((height(node.right) - height(node.left)) == 2) {
if(key.compareTo(node.right.item) > 0)
node = singleRotateLeft(node);
else
node = doubleRotateLeft(node);
}
}
return node;
}
สมมตวาเราน าขอมล [8, 12, 14, 18, 20, 23, 33, 44, 48, 50, 52] เขาส tree เรากจะได tree ดงทเหนในภาพท 7.20 และสมมตตอไปวาเราตองการทจะลบ node 44 ออกจาก tree สงทเกดข นคอ เราจะเดนเขาหา tree ทางดานขวาจนกวาจะเจอ node 44 ในระหวางทางเราจะจ าคาของ lastNode และ deleteNode ไว ขนตอนทเกดข นไดถกจ าลองไวดงน โดยทครงแรก node ทสงเขาไปมคาเทากบ 18 (root node ของ tree)
Self-Balanced Binary Search Trees บทท 7
188
1. lastNode = 18, เดนลงทางขวา 2. ก าหนดให deleteNode = lastNode (18), เดนลงทางขวาอก 3. เจอ node 44 จงก าหนดให lastNode = 44, เดนลงทางขวาอก 4. ก าหนดให deleteNode = lastNode (44), เดนลงทางขวาอก 5. เจอ node 50 จงก าหนดให lastNode = 50, เปลยนการเดนไปเปนทางซาย 6. เจอ node 48 จงก าหนดให lastNode = 48, เดนลงทางซายอก 7. เจอ null จงยอนกลบจาก recursion ณ เวลาน lastNode มคาเทากบ node แตคาของ
deleteNode และ lastNode ไมเทากนดงนนเราจงท าการสลบคาของ node ทงสอง แลวจงยอนกลบออกจาก method delete()
ซงผลทเราไดเมอเสรจส นการเรยกแลวท าให node 44 ทเราตองการลบมคาเปน 48 ซงเปนการแทน node ทลบออกดวยคาทนอยทสดทางดานขวาของ node ทตองการลบ ทศทางของการเดนเขาหา tree ไมวาจะเปนทางซาย หรอทางขวาขนอยกบคาของ node ทเจอ ณ เวลานน ซงเกดจากการเปรยบเทยบของประโยค //key to delete is on the left
if(key.compareTo(node.item) < 0)
node.left = delete(key, node.left);
//key is on the right
else {
deleteNode = node; //potential node to delete
node.right = delete(key, node.right);
}
ภาพท 7.20 AVL tree ทสรางจากโปรแกรมตวอยาง
18
12 44
8 14
33
23
20 52
50
48
lastNode
deleteNode
ทศทางการเดนของ
lastNode และคาทเปลยนไป คอ 18, 44, 50, และ 48
deleteNode จะหยดการเปลยนคาเมอมาถง node 44
บทท 7 Self-Balanced Binary Search Trees
189
ภาพท 7.21 AVL tree หลงจากการลบ node 44
เพอใหเกดความเขาใจยงข นในเรองของการหมน node เราจะน าขอมล 21 และ 98 เขาส tree ในภาพท 7.21 ซงท าใหเราไดภาพของ tree ตามล าดบดงน
ภาพท 7.22 การน า node 21 เขาส tree ท าให tree ไม balance
18
12 48
8 14
33
23
20 52
50
การน า node 21 เขาส tree ท าใหเกดความไมสมดล (กรณท 4: left of
right)
18
12 48
8 14
33
23
20 52
50
21
ท าใหเราตองหมน node 48
Self-Balanced Binary Search Trees บทท 7
190
ภาพท 7.23 tree หลงจากการหมน node 48 ยงไมมความสมดล
ภาพท 7.24 tree หลงจากการหมน node 18 เกดความสมดลของ tree
หลงจากการหมน node 48 tree กยงไมมความสมดล เรา
ตองหมน node 18 ไปทางซาย
อกครง
18
12 23
8 14
21
20
33 50
48
52
23
18 48
12 20
8
33
14 52
50
21
บทท 7 Self-Balanced Binary Search Trees
191
ภาพท 7.25 tree หลงจากการ insert 98
ผอานควรทดลองการน าขอมลเขา และออกจาก AVL tree ดวยขอมลตาง ๆ เพอท าความเขาใจกบการท างานของการน าขอมล และการน าขอมล รวมไปถงการหมน node ในกรณตาง ๆ เพอให เกดความเขาใจมากยงข น เราไดรวบรวม code ทงหมดของ AVLNode.java และ AVLTree.java มาไวใหผอานอกครงหนง
1: /**
2: Structure of node for AVL tree
3: */
4:
5: public class AVLNode<T extends Comparable> {
6: protected T item;
7: protected AVLNode<T> left, right;
8: protected int height;
9:
10: //for empty node
11: AVLNode() {
12: item = null;
13: left = right = null;
14: height = 0;
15: }
16:
17: //for node with item assigned
18: AVLNode(T item) {
19: this.item = item;
subtree ทไมมความ balance หลงจากการใส node 98 เราตองท า
การหมน node 50 ไปทางซายหนง
ครง
subtree หลงจากการหมน node 50
23
18 48
12 20
8
33
14 52
50
21
98
50
52
48
98
33
Self-Balanced Binary Search Trees บทท 7
192
20: left = right = null;
21: height = 0;
22: }
23:
24: //calculate number of nodes, recursively
25: public int size(AVLNode<T> t) {
26: if(t == null)
27: return 0;
28: else
29: return size(t.left) + size(t.right) + 1;
30: }
31:
32: //print data of node in Pre-order fashion
33: public void preorderPrint() {
34: System.out.print(item + " ");
35: if(left != null) {
36: left.preorderPrint();
37: }
38: if(right != null) {
39: right.preorderPrint();
40: }
41: }
42:
43: //print data of node in In-order fashion
44: public void inorderPrint() {
45: if(left != null) {
46: left.inorderPrint();
47: }
48: System.out.print(item + " ");
49: if(right != null) {
50: right.inorderPrint();
51: }
52: }
53:
54: //print data of node in Post-order fashion
55: public void postorderPrint() {
56: if(left != null) {
57: left.postorderPrint();
58: }
59: if(right != null) {
60: right.postorderPrint();
61: }
62: System.out.print(item + " ");
63: }
64: }
1: /**
2: Structure for AVL Tree
3: Using AVLNode as storage
4: */
5:
6: class AVLTree<T extends Comparable<? super T>> {
7: protected AVLNode<T> root;
8: private AVLNode<T> lastNode; //used in delete(T key)
9: private AVLNode<T> deleteNode; //same
10: private int count = 0;
11:
12: //default constructor
13: AVLTree() {
14: root = null;
15: }
16:
17: //returning height of AVL tree
18: public int height(AVLNode<T> t) {
19: if(t == null)
20: return -1;
21: else
22: return Math.max(height(t.left), height(t.right)) + 1;
23: }
24:
25: //insert into AVL tree
26: public void insert(T key) {
27: root = insert(key, root);
28: }
บทท 7 Self-Balanced Binary Search Trees
193
29:
30: //internal method to insert key into AVL tree
31: private AVLNode<T> insert(T key, AVLNode<T> node) {
32: //tree is empty, just insert
33: if(node == null) {
34: node = new AVLNode<T>(key);
35: }
36: //key is less than item, so we go left
37: else if(key.compareTo(node.item) < 0) {
38: node.left = insert(key, node.left);
39: if(node.left != null) {
40: //height is not balance,perform rotation
41: if((height(node.left) – height(node.right)) == 2) {
42: if(key.compareTo(node.left.item) < 0)
43: node = singleRotateRight(node);
44: else
45: node = doubleRotateRight(node);
46: }
47: }
48: }
49: //key is greater than item, so we go right
50: else if(key.compareTo(node.item) > 0) {
51: node.right = insert(key, node.right);
52: if(node.right != null) {
53: //height is not balance, perform rotation
54: if((height(node.right) – height(node.left)) == 2) {
55: if(key.compareTo(node.right.item) > 0)
56: node = singleRotateLeft(node);
57: else
58: node = doubleRotateLeft(node);
59: }
60: }
61: }
62: else ; //duplicates not allow!
63:
64: return node;
65: }
66:
67: //delete node with given key from AVL tree
68: //replace deleted node with the minimum
69: //node on its right subtree
70: public void delete(T key) {
71: lastNode = null;
72: deleteNode = null;
73: root = delete(key, root);
74: }
75:
76: //internal method to delete node with given key
77: //uses inorder predecessor as replacement node
78: private AVLNode<T> delete(T key, AVLNode<T> node) {
79: //tree is empty, cannot delete
80: if(node == null) {
81: return null;
82: }
83: lastNode = node; //last node seen
84:
85: //key to delete is on the left
86: if(key.compareTo(node.item) < 0)
87: node.left = delete(key, node.left);
88: //key is on the right
89: else {
90: deleteNode = node; //potential node to delete
91: node.right = delete(key, node.right);
92: }
93:
94: //returning from the calls
95: if(node == lastNode) {
96: //make sure its our target node
97: if(deleteNode != null && key.equals(deleteNode.item)) {
98: //node is rightmost in its subtree
99: if(deleteNode == lastNode) {
100: //return left subtree
101: node = node.left;
102: }
Self-Balanced Binary Search Trees บทท 7
194
103: //node is not rightmost, replace deleted node
104: //with minimum node on the right subtree
105: else {
106: //swap data
107: T tmp = deleteNode.item;
108: deleteNode.item = lastNode.item;
109: lastNode.item = tmp;
110: //return right subtree
111: node = node.right;
112: }
113: }
114: }
115: //adjust the nodes
116: else {
117: //left tree is taller than right tree
118: if((height(node.left) - height(node.right)) == 2) {
119: if(key.compareTo(node.left.item) < 0)
120: node = singleRotateRight(node);
121: else
122: node = doubleRotateRight(node);
123: }
124: //right tree is taller than left tree
125: if((height(node.right) - height(node.left)) == 2) {
126: if(key.compareTo(node.right.item) > 0)
127: node = singleRotateLeft(node);
128: else
129: node = doubleRotateLeft(node);
130: }
131: }
132: return node;
133: }
134:
135: //LEFT-OF-LEFT
136: //insert in the left subtree of the left child of X
137: //do a single rotation with left child (rotate right)
138: //e.g. insert the followings 20, 18, and 12
139: private AVLNode<T> singleRotateRight(AVLNode<T> X) {
140: AVLNode<T> tmp = new AVLNode<T>();
141: tmp = X.left;
142: X.left = tmp.right;
143: tmp.right = X;
144:
145: return tmp;
146: }
147:
148: //RIGHT-OF-RIGHT
149: //insert in the right subtree of the right child of X
150: //do a single rotation with right child (rotate left)
151: //e.g. insert the followings 12, 18, and 20
152: private AVLNode<T> singleRotateLeft(AVLNode<T> X) {
153: AVLNode<T> tmp = new AVLNode<T>();
154: tmp = X.right;
155: X.right = tmp.left;
156: tmp.left = X;
157:
158: return tmp;
159: }
160:
161: //RIGHT-OF-LEFT
162: //insert in the right subtree of the left child of X
163: //do single rotation on left child, then rotate right on X
164: //e.g. insert the followings 12, 4, and 8
165: private AVLNode<T> doubleRotateRight(AVLNode<T> X) {
166: X.left = singleRotateLeft(X.left);
167: return singleRotateRight(X);
168: }
169:
170: //LEFT-OF-RIGHT
171: //insert in the left subtree of the right child of X
172: //do single rotation on right child, then rotate left on X
173: //e.g. insert the followings 12, 44, and 18
174: private AVLNode<T> doubleRotateLeft(AVLNode<T> X) {
175: X.right = singleRotateRight(X.right);
176: return singleRotateLeft(X);
บทท 7 Self-Balanced Binary Search Trees
195
177: }
178:
179: //locate minimum node
180: public T findMin(AVLNode<T> node) {
181: if(node != null) {
182: while(node.left != null)
183: node = node.left;
184: }
185: return node == null ? null : node.item;
186: }
187:
188: //locate maximum node
189: public T findMax(AVLNode<T> node) {
190: if(node != null) {
191: while(node.right != null)
192: node = node.right;
193: }
194: return node == null ? null : node.item;
195: }
196:
197: //remove minimum node
198: private AVLNode<T> removeMin(AVLNode<T> node) {
199: if(node == null) {
200: System.out.println("ERROR! tree is empty.");
201: return null;
202: }
203: else if(node.left != null) {
204: node.left = removeMin(node.left);
205: return node;
206: }
207: else
208: return node.right;
209: }
210:
211: //remove maximum node
212: private AVLNode<T> removeMax(AVLNode<T> node) {
213: if(node == null) {
214: System.out.println("ERROR! tree is empty.");
215: return null;
216: }
217: else if(node.right != null) {
218: node.right = removeMax(node.right);
219: return node;
220: }
221: else
222: return node.left;
223: }
224:
225: //print tree in pre-order fashion
226: public void printAVLTree() {
227: System.out.print("Items: ");
228: root.preorderPrint();
229: System.out.println();
230: }
231: }
โปรแกรมทดสอบการท างานของ AVL tree 1: /**
2: Testing AVL Tree
3: */
4:
5: class TestAVLTree {
6: public static void main(String[] args) {
7: AVLTree<Integer> tree = new AVLTree<Integer>();
8: int []data = {8, 12, 14, 18, 20, 23, 33, 44, 48, 50, 52};
9:
10: //populate tree with data
11: for(int i = 0; i < data.length; i++) {
12: tree.insert(data[i]);
13: }
14: tree.printAVLTree();
15:
Self-Balanced Binary Search Trees บทท 7
196
16: //delete 44 and insert 21 and 98
17: tree.delete(44);
18: tree.printAVLTree();
19: tree.insert(21);
20: tree.printAVLTree();
21: tree.insert(98);
22: tree.printAVLTree();
23: }
24: }
ผอานควรทดสอบการท างานของ AVL Tree ดวยขอมลตาง ๆ กนในการน าขอมลเขา และการดงขอมลออก เพอใหเกดความเขาใจถงการท างานอยางแทจรง โดยเฉพาะการหมนของ node หลงจากการน า node เขาและการน า node ออก
7.2 Red-Black Tree Red-Black Tree เปน Tree ทคดคนข นโดย Rudolf Bayer ในป 1972 และมชอเรยกในครงนนวา Symmetric binary B-tree ซงเปน tree ทมความสมดลอกตวหนงทใชคณสมบตของ BST เปนตวก าหนดการน าขอมลเขา เชนเดยวกบ AVL Tree และจะท าการปรบความสมดลโดยอตโนมตเชนเดยวกน แตจะมขอก าหนดอน ๆ อกบางประการทท าให Red-Black Tree ไม
เหมอนกบ Tree ทมความสมดลอน ๆ 7.2.1 ขอก าหนดของ Red-Black Tree Binary Search Tree ทมความสมดลพรอมทจะเปน Red-Black Tree จะตองมคณสมบตดงน
1. Node สามารถเปนไดทงสแดง หรอสด า
2. Node ทอยบนสด (root) จะตองเปนสด าเทานน 3. Node ทอยนอกสด หรอทเรยกวา null node เปนไดแคสด าเทานน 4. ถา node เปนสแดง ลกของ node นจะตองเปนสด าทงค 5. จ านวนของ node ทเปนสด าทก ๆ เสนทางจาก root ไปยง node นอกสด (null node)
จะเทากน
ภาพท 7.26 Red-Black Tree (แสดง null nodes)
ในภาพท 7.26 node ทมสแดงจะเปนเสนประ node ทมสด าจะเปนเสนทบ สวน null node จะเปน node ทบทไมมขอมลอยภายใน ในการน า node เขาส Red-Black Tree นนเราจะตองอาศยการใช null node เปนตวชวยท าใหขอก าหนดของ Red-Black Tree เปนจรงอยเสมอ ส งทเราตองค านงถงในการน าขอมลเขากคอ เราจะตองรกษาไวซ งคณสมบตของ Red-Black Tree ดงทไดกลาวไวกอนหนาน สของ node จะตองถกตอง และตองมความสมดลเชนเดยวกนกบ
Inside child
Aunt or Uncle of X
Outside child (X)
Parent
Grandparent of X 23
18
12
8 14
4
19 52
null nodes
บทท 7 Self-Balanced Binary Search Trees
197
AVL Tree ดงนนเราจะตองมการเปลยนสของ node และ(หรอ) หมน node เพอรกษาไวซ งขอก าหนดดงกลาว 7.2.2 การน าขอมลเขา ปจจยทเราตองค านงอยตลอดเวลาในการน าขอมลเขากคอ ต าแหนงของ node ทตองน าเขา เนองจากวาเราตองการทจะใหการน า node เขาเปนไปตามเงอนไขเราจะก าหนดให node ทน าเขามสแดงเสมอ และการน าเขาเราจะใชหลกการของ Binary Search Tree เปนขอก าหนด คอน า node เขาตามปกต ตรวจสอบและปรบ tree ใหเปนไปตามขอก าหนดของ Red-Black Tree ทหลง เพราะฉะนนการน าขอมลเขา จะตองค านงถงสถานการณตอไปน (ดภาพท 7.27 ประกอบ)
1. parent ของ node ทน าเขาเปนสด า 2. parent ของ node ทน าเขาเปนสแดง และ node ทน าเขาเปน node ทอยดานนอก
(outside) 3. parent ของ node ทน าเขาเปนสแดง และ node ทน าเขาเปน node ทอยดานใน
(inside) เราจะพดถงการน าขอมลเขาเพยงดานใดดานหนงเทานน เชนในภาพท 7.27 เปนการแสดงการน าเขาทางดานซายของ tree เทานน เพราะวาการน าเขาทางดานขวากเหมอนกน เพยงแคกลบกนเทานน (symmetric)
ภาพท 7.27 ความเปนไปไดของต าแหนงของ node หลงจากการน าเขา (ไมแสดง null nodes)
7.2.3 กรณท 1 Parent ของ node ทน าเขาเปนสด า เราไดก าหนดไวกอนหนานวา node ใหมทน าเขาจะเปนสแดงเสมอ เพราะฉะนนการน า node เขาจะไมท าใหเกดปญหาใด ๆ เชน ม node ทเปนสแดงตอเนองกน 2 node (ขอก าหนดท 4) หรอจ านวนของ node ทเปนสด าจาก root ไปยง null node มไมเทากน (ขอก าหนดท 5) เราจง
ไมตองปรบปรง tree เพราะการน าขอมลเขาถกตองแลว 7.2.4 กรณท 2 Parent ของ node ทน าเขาเปนสแดง และ node ทน าเขาอยดานนอก ในกรณนเราจะตองตรวจสอบวา node ทน าเขามานนท าใหเกดความสมดล หรอไม ถามความสมดลส งทเราตองท ากคอ เปลยนสของ parent และพ (หรอนอง) ของ parent ใหเปนสด า แตถาท าใหเกดความไมสมดลเราตองท าการเปลยนสพรอมทงการหมน node ดงภาพท 7.28 ถง 7.32
G
P
50
12
25
X X
G
P
50
12
25
G
P
50
70
75
X
P เปนสด า P เปนสแดง ต าแหนงของ node อยดานนอก
P เปนสแดง ต าแหนงของ node อยดานใน
Self-Balanced Binary Search Trees บทท 7
198
ภาพท 7.28 node X อยดานนอก และ parent ของ X เปนสแดงและการเปลยนสของ node ยงท าให tree เปน Red-
Black tree อย เรากไมตองท าการหมนใด ๆ (ไมแสดง null nodes)
ภาพท 7.29 node X ท าใหเกดความไมสมดล ภาพท 7.30 การเปลยนสของ node 18 และ node 25 ท า
ใหเกดความไมสมดล (ไมแสดง null nodes)
G
P
50
12
25
X
75
G
P
50
12
25
X
75
X
12
G
P
50
18
25 75
X
12
G
P
50
18
25 75
บทท 7 Self-Balanced Binary Search Trees
199
ภาพท 7.31 เปลยนสเสรจเรากหมน node 25 ไปทางขวา ภาพท 7.32 Red-Black Tree ทสมบรณ
(ไมแสดง null nodes)
7.2.5 กรณท 3 Parent ของ node ทน าเขาเปนสแดง และ node ทน าเขาอยดานใน ถา node ทน าเขาอยดานในเราจะตองท าการหมน node ทเปน parent ของ node ทน าเขาไปทางซาย เปลยนสของ node เหมอนทเราท าในกรณท 2 หลงจากนนหมน node ไปทางขวา ดงทแสดงในภาพประกอบน
ภาพท 7.33 การหมนซายของ node 12 ภาพท 7.34 หลงจากการหมน node และเปลยนส
(ไมแสดง null nodes)
12
50
18
25
75
X
12
G
P
50
18
25 75
X
G
P
50
12
25 75
18
X
G
P
50
18
25 75
12
Self-Balanced Binary Search Trees บทท 7
200
ภาพท 7.35 การหมน node 25 ไปทางขวา ภาพท 7.36 Red-Black Tree ทสมบรณ
(ไมแสดง null nodes)
7.2.6 การออกแบบ code ส าหรบการน าขอมลเขา ในการออกแบบ code นนเราจะตองเพม node ใหกบ Red-Black Tree เพอเอาไวใชเกบขอมลของ parent และเปนตวเชอมตอของ node ตาง ๆ ภายใน tree เมอมการโยกยาย node ดงนนเราจงเปลยน code ของ node ใหเปน
1: /**
2: Node for Red-black tree
3: */
4:
5: class RBNode<T> {
6: protected final int RED = 0, BLACK = 1;
7: protected RBNode<T> parent;
8: protected RBNode<T> left;
9: protected RBNode<T> right;
10: protected int color;
11: protected T key;
12:
13: //constructor for a null node (always black)
14: //left and right children are also null nodes
15: //see RBTree's constructor
16: public RBNode(T key) {
17: this.key = key;
18: color = BLACK;
19: parent = null;
20: }
21:
22: //constructor for new red node which has a parent and
23: //left and right nodes as nullNode
24: public RBNode(T key, RBNode<T> node, RBNode<T> nullNode) {
25: this.key = key;
26: color = RED;
27: parent = node;
28: left = right = nullNode;
29: }
30: }
Class RBNode ของเรามแค constructor 2 ตวส าหรบการสราง node ใหมกระบวนการอน ๆ จะอยใน class RBTree
Constructor ตวแรกเราใชส าหรบการสราง null node ซงจะเปนไดแค node ทมสด าเทานน สวน constructor ตวทสองเราใชส าหรบการสราง node ใหมทตองการน าเขา ซงตามขอก าหนดแลวจะเปนสแดงเสมอ
50
12
18 75
25
X
G
P
50
18
25 75
12
บทท 7 Self-Balanced Binary Search Trees
201
เราเรมสราง code ของการน าขอมลเขาทมลกษณะการน าเขาคลายกบ Binary Search Tree ซงมสวนประกอบดงน (อยใน class RBTree) //insert key into tree
public void insert(T key) {
RBNode<T> current = root;
RBNode<T> parent = null;
RBNode<T> newNode;
//locating a spot to insert as well as a parent
while(current != nullNode) {
//no duplicates allowed
if(key.equals(current.key))
return;
//remember parent
parent = current;
if(key.compareTo(current.key) < 0)
current = current.left;
else
current = current.right;
}
//creates a new red node
newNode = new RBNode<T>(key, parent, nullNode);
//tree exists, insert as binary search tree
if(parent == null)
root = newNode; //first time insertion
else {
if(key.compareTo(parent.key) < 0)
parent.left = newNode;
else
parent.right = newNode;
}
//restore red-black properties after insertion
restoreAfterInsertion(newNode);
}
เชนเดยวกบการน าขอมลเขาส Binary Search Tree เราตองหาต าแหนงทเหมาะสมใหเจอกอน ซงเราไดท าใน while/loop โดยเราจะท าการจ า node ทเปน parent ของ node ทเราจะน าเขาดวย หลงจากนนเรากสราง node ใหมดวยค าสง newNode = new RBNode<T>(key, parent, nullNode);
nullNode ทเราสงไปให constructor ของ RBNode สรางจากค าสง nullNode = new RBNode<T>(null); ทอยใน contructor ของ class RBTree เมอได node ใหมแลวเราตองท าการตรวจสอบวา tree ของเราเปน tree ทมขอมลอยหรอไม ถาไมมเรากเพยงแตก าหนดให root ชไปท node ใหมน แตถามเรากตองดวาควรจะเชอม node ใหมนเขาทางขวา หรอทางซายของ parent เมอเชอมแลวเรากเรยกใช method restoreAfterInsertion() เพอท าการปรบ tree ใหเปนไปตามขอก าหนดของการเปน Red-Black Tree ตอไปซงม code ดงน //restore tree after insertion
private void restoreAfterInsertion(RBNode<T> X) {
//perform maintenance only when parent of X is red
while(X != root && X.parent.color == RED) {
//node was inserted on the left of the tree
if(X.parent == X.parent.parent.left) {
//y is X's parent sibling (aunt or uncle?)
RBNode<T> y = X.parent.parent.right;
//aunt or uncle of X is RED and X is an
//outside grandchild, do color flip
if(y.color == RED) {
X.parent.color = BLACK;
y.color = BLACK;
X.parent.parent.color = RED;
X = X.parent.parent;
}
Self-Balanced Binary Search Trees บทท 7
202
//aunt or uncle is black we have two cases
//either X is an inside or an outside grandchild
//for the first case we rotate left on X, re-color
//and rotate right on X's grandparent
//for the later, we re-color and rotate right
//on X' grandparent
else {
//X is a right grandchild (inside)
if(X == X.parent.right) {
X = X.parent;
rotateLeft(X);
}
//X is a left grandchild (outside)
//change X's parent's color to black
//change X's grandparent's color to red
//and perform a right rotation on X's grand
X.parent.color = BLACK;
X.parent.parent.color = RED;
rotateRight(X.parent.parent);
}
}
//node was inserted on the right of the tree
//just a mirror-image of the code above
else {
RBNode<T> y = X.parent.parent.left;
//aunt or uncle is red
if(y.color == RED) {
X.parent.color = BLACK;
y.color = BLACK;
X.parent.parent.color = RED;
X = X.parent.parent;
}
//aunt or uncle is black
else {
if(X == X.parent.left) {
X = X.parent;
rotateRight(X);
}
X.parent.color = BLACK;
X.parent.parent.color = RED;
rotateLeft(X.parent.parent);
}
}
}
//if somehow during our re-coloring X turned to red,
//and X became root, we must change it to black
root.color = BLACK;
}
หลงจากทเราทดสอบดวยโปรแกรม TestRedBlackTree.java
1: /**
2: Testing module for Red-Black Tree
3: */
4:
5: class TestRedBlackTree<T> {
6: public static void main(String[] args) {
7: RBTree<Integer> t = new RBTree<Integer>();
8: int[] key = {4, 7, 12, 15, 3, 5, 14, 18, 16, 17};
9:
10: for(int data : key) {
11: t.insert(data);
12: t.printTree();
13: }
14: }
15: }
ผลลพธทเราไดคอ 4(B)
4(B) 7(R)
บทท 7 Self-Balanced Binary Search Trees
203
7(B) 4(R) 12(R)
7(B) 4(B) 12(B) 15(R)
7(B) 4(B) 3(R) 12(B) 15(R)
7(B) 4(B) 3(R) 5(R) 12(B) 15(R)
7(B) 4(B) 3(R) 5(R) 14(B) 12(R) 15(R)
7(B) 4(B) 3(R) 5(R) 14(R) 12(B) 15(B) 18(R)
7(B) 4(B) 3(R) 5(R) 14(R) 12(B) 16(B) 15(R) 18(R)
14(B) 7(R) 4(B) 3(R) 5(R) 12(B) 16(R) 15(B) 18(B) 17(R)
เราก าหนดใหผลลพธในบางบรรทดเปนตวหนาเพอใหผอานสงเกตวามการปรบปรง tree ให เปนไปตามขอก าหนด โดยครงแรกนนเกดข นเมอเราน าเอา 12 เขาส tree ซงท าใหเราได node ทเปนสแดง 2 node ตดกนและ node ทน าเขาเปน node ทอยดานนอกเราจงตองท าการเปลยน
ส parent ของ node นใหเปนสด า และเปลยนสของ grandparent ของ node นใหเปนสแดง พรอมทงหมน node ทเปน grandparent ไปทางซายหนงครง (ซงเปนการท ากลบกนของภาพท 7.33 – 7.36) หลงจากนนการปรบปรงกเกดข นอกครงหนงหลงจากทเราน าเอา 14 เขาส tree ซงท าใหเราตองท าการหมน node ทเปน parent ของ node ทน าเขาไปทางขวาหนงครง หลงจากนนกเปลยนส
ของ node ทเปน parent ใหเปนสด า และเปลยนสของ grandparent ใหเปนสแดง แลวจงท า
การหมน node ไปทางซายหนงครง (ดภาพประกอบ)
ภาพท 7.37 การน า node 14 เขาส tree และการหมน node ไปทางขวา (ไมแสดง null nodes)
ภาพท 7.38 การหมน node ไปทางซายหลงจากการเปลยนส ภาพท 7.39 Red-Black Tree ทสมบรณ
(ไมแสดง null nodes)
7
14
4 12
15 5 3
7
15
4 12
14 5 3
7
15
4 14
12 5 3
7
15
4 12
14 5 3
Self-Balanced Binary Search Trees บทท 7
204
หลงจากนนเรากมการปรบปรง tree อกสามครงคอ หลงจากการน า node 16, node 18 และ node 17 เขาส tree ซงเราจะทงไวใหผอานไดลองตรวจสอบดวยการเขยนภาพขนมาเอง 7.2.7การลบ node การลบ node ออกจาก Red-Black Tree กท าคลาย ๆ กบการลบ node ออกจาก BST กลาวคอ หา node ทตองการลบใหเจอ เมอเจอแลวกตองหา node ทจะตองมาแทน ซง node ทจะน ามาแทนนนจะตองรกษาคณสมบตของ BST ไวใหได และกเปนเรองทไมยากอะไร เพราะเราไดท ามากอนแลวในเรองของ AVL Tree แตส งส าคญหลงจากการดง node ออกกคอการรกษาขอก าหนดของ Red-Black Tree ในการลบ node ออกจาก tree ทใชเงอนไขของ BST เปนตวก าหนดการน าขอมลเขานน เราไมไดลบ node ทตองการออกจรง แตเปนการหา node ทมขอมลทกฎก าหนดไวมาแทนท (node ทมคานอยทสดทางขวา หรอ node ทมคามากทสดทางซาย) แลวจงลบ node ทหาได ทงไปซง node ทวานจะเปน node ทอยดานนอกสดเสมอ (leaf node) ดงนนการลบในลกษณะนของ Red-Black tree จงท าใหเสนทางจาก root node ไปยง leaf node มจ านวนของ black node นอยกวาเสนทางอน ๆ อยหนง node ถา node (V ในภาพ) เปนสด า
ภาพท 7.40 double black ทก าหนดขนหลงจากการลบ
ดงนนเพอใหการลบ node ของเราคงไวซ งขอก าหนดของการเปน Red-black Tree เราจงก าหนดให node นเปน double black node (node X ในภาพ) และเราจะท าการปรบปรง tree ใหเปน Red-black Tree ตอไปตามเงอนไขใน 7.2.3, 7.2.4, และ/หรอ 7.2.5 (ผอานควรตรวจสอบดวาถา node ทถกลบออกเปนสแดงเรากไมตองท าอะไรเลย)
ภาพท 7.41 การก าหนดให node ทถกลบเปน double black
node ทตองถกลบออกหลงจากการแทนคาแลว
X
V
X
เสนประ = double black
6
3
4
8
W U
X
6
3
4
X
การลบ node 8 ออกท าใหเกดเหตการณทเราเรยกวา double black
เราจงตองท าการปรบปรง tree ให
เปนไปตามขอก าหนด
บทท 7 Self-Balanced Binary Search Trees
205
ภาพท 7.42 การก าหนดให node ทถกลบเปน double black
สรปข นตอนการลบ node ออกจาก Red-black Tree
1. ลบ node ตามปกตดวยกฎของ Binary Search Tree 2. ก าหนดให node X (node ทถกเลอกในกระบวนการลบของ BST) เปน double black
node ถา node X เปนสด า แลวด าเนนการตามเงอนไขใน 7.2.3, 7.2.4 และ/หรอ 7.2.5 ตามความเหมาะสม
หมายเหต:
1. ผอานอยาลมวา node X (double black node) เปนเพยง node สมมตทเราสรางขนเพอการปรบปรง tree ใหเปนไปตามเงอนไข ไมใช node ทมอยจรงใน tree
2. ภาพทแสดงเปน snap shot ของ tree ซงมความหมายวาอาจม sub-tree อยภายใต node ใด node หนงกได หรออาจเปน node ดานนอกทม null node เปน leaf
เง อนไขของการปรบปรง tree เมอมการลบเกดข น 7.2.8 Sibling (พหรอนอง) ของ node X มสด าและมลกสแดงหนง node
ภาพท 7.43 สวนหนงของ tree หลงจากการลบ node
ถาการลบ node ออกจาก tree ท าให
เกดสถานการณแบบน คอ node w มสด าและมลกเปนสแดงหนง node เรา
จะตองท าการเปลยนสของ node 5 ใหเปนสด า เปลยนสของ node 4 ให เปนสแดง ท าการหมนซายท node 4 หลงจากนนกท าการเปลยนส และ
หมน node ไปทางขวา ดงทแสดงในรปถดไป
ลกของ node 4 มสแดง
Sibling ของ node X มส
ด า
7
4
5
W
X
7
15
4 14
12 5 3
12
15
4 14
5 3
ตองการลบ
node 7
Node 12 เปน node ทเราตองน าไปแทน
Node 12 เดมเปนสด า เราจงตองก าหนดให
เปน double black
U
X X
U
Self-Balanced Binary Search Trees บทท 7
206
ภาพท 7.44 node ถกเปลยนสและหมนไปทางซาย ภาพท 7.45 หลงจากหมนซาย กอนการหมนขวา
ภาพท 7.46 หลงจากการหมน node 7 ไปทางขวา
7.2.9 Sibling ของ node X มสด าและลกท งสองกมสด า ในกรณทเหตการณนเกดข น ส งทเราตองท ากเปนเพยงแคการเปลยนสของ node เทานน ดงภาพทแสดงน
ภาพท 7.47 การเปลยนส node
การเปลยนสของ node ดงทแสดงในภาพอาจท าใหเกดปญหาขนไดเหมอนกน คอ จ านวนของ node ด าในเสนทางนมนอยกวาเสนทางอนหนงตว ดงนนเราอาจตองกลบไปแกไข tree ของเราใหมขอก าหนดทถกตอง ดงทเราไดแสดงใหดในกรณกอนหนาน (7.2.8)
7
4
5
W
7
5
4
W
X X
5
4 7
16
15 W
16
15 W
X X
บทท 7 Self-Balanced Binary Search Trees
207
7.2.10 Sibling ของ node X มสแดง
ภาพท 7.48 sibling ของ node X มสแดง ภาพท 7.49 tree หลงจากการเปลยนส node 5 และ node 14 พรอม ทงการหมน node 14 หลงจากนน
ภาพท 7.50 tree หลงจากการเปลยนสเนองจาก node 7 มลกเปนสด า (null nodes ทงค)
สงทเกดข นจากการท sibling ของ node X เปนสแดงนนมกระบวนการหลก ๆ อยสองขนตอนคอ การเปลยนสของ node กอนการหมน และการเปลยนสของ node หลงจากการหมน ผอานจะเหนวา หลงจากทเราหมน node เรยบรอยแลว node 14 เปนสแดง และ node 7 เปนสด า (ภาพท 7.48 – 7.50) แตเนองจากวา node 7 เปน leaf node ทม null node อยแลวสอง node ดงนนเราตองท าการเปลยน node 14 เปนสด า และ node 7 เปนสแดง เพอใหถกตองตามขอก าหนด หลาย ๆ ครงการลบ node ออกจาก tree มกจะเกยวของกบกรณตาง ๆ มากกวาหนงกรณ ดงนนการเขยน code เพอรองรบการลบ node จะตองระมดระวงขนตอนเหลานนใหด สวนไหนทนาจะมากอนกตองมากอน สวนไหนทอาจใชรวมกนหลงจากกระบวนการอนเสรจส นลง กตองอยตามมา ซงถาเราออกแบบกระบวนการเหลานดแลว code ของเรากจะไมยาวจนเกนไปนก Code ทงหมดของการลบ node มดงน
14
5
7
W
4
5
4 14
7
W
node W เปนสด าไมได
เพราะม null node ทเปนสด าอย
X
X
5
4 14
7
Self-Balanced Binary Search Trees บทท 7
208
//delete a node from tree
public void delete(T key) {
RBNode<T> X;
RBNode<T> successor; //replacement node
RBNode<T> deleteNode; //node to be deleted
//find node to delete
deleteNode = root;
while(deleteNode != nullNode) {
//found a target node
if(key.equals(deleteNode.key))
break;
//not yet found, keep searching
else {
if(key.compareTo(deleteNode.key) < 0)
deleteNode = deleteNode.left;
else
deleteNode = deleteNode.right;
}
}
//node to delete is not in a tree, abort
if(deleteNode == nullNode) return;
//successor has a nullNode as a child (deleteNode is leaf)
if(deleteNode.left == nullNode ||
deleteNode.right == nullNode)
successor = deleteNode;
//find a successor with a nullNode as a child
//locate minimum node on the right
else {
successor = deleteNode.right;
while(successor.left != nullNode)
successor = successor.left;
}
//X is successor's only child
if(successor.left != nullNode)
X = successor.left;
else
X = successor.right;
//removing successor from parent chain
X.parent = successor.parent;
if(successor.parent != null) {
if(successor == successor.parent.left)
successor.parent.left = X;
else
successor.parent.right = X;
}
else
root = X;
//replace the key (keep the color)
if(successor != deleteNode)
deleteNode.key = successor.key;
//restore red-black properties after deletion
if(successor.color == BLACK)
restoreAfterDelete(X);
}
//restore tree after deletion
private void restoreAfterDelete(RBNode<T> X) {
//maintenance needed when black node was deleted
while(X != root && X.color == BLACK) {
//handle left side
if(X == X.parent.left) {
//w is a sibling of X (a right child)
RBNode<T> w = X.parent.right;
//sibling is red
if(w.color == RED) {
w.color = BLACK;
X.parent.color = RED;
rotateLeft(X.parent);
w = X.parent.right;
บทท 7 Self-Balanced Binary Search Trees
209
}
//both children of w are black, change parent to red
//and reset X to its parent
if(w.left.color == BLACK &&
w.right.color == BLACK) {
w.color = RED;
X = X.parent;
}
//they are not both black, re-color and
//do double rotations, iff right child of w
//is black, otherwise do a left rotation
else {
//right child of w is black
if(w.right.color == BLACK) {
w.left.color = BLACK;
w.color = RED;
rotateRight(w);
w = X.parent.right;
}
//right child of w is red
//change w's color to its parent
//change its parent's color to black
//change right child's color to black
//rotate left on its parent
w.color = X.parent.color;
X.parent.color = BLACK;
w.right.color = BLACK;
rotateLeft(X.parent);
X = root; //set this new tree
}
}
//handle right side
//a mirror-image of code above
else {
RBNode<T> w = X.parent.left;
if(w.color == RED) {
w.color = BLACK;
X.parent.color = RED;
rotateRight(X.parent);
w = X.parent.left;
}
//if left and right nodes are black, their
//parent must be red
if(w.right.color == BLACK &&
w.left.color == BLACK) {
w.color = RED;
X = X.parent;
}
//w has one red node, two cases to consider
//if node on the left is black, we change color
//and rotate left on w, then we recolor and
//rotate right one more time
//if node on the left is red, we change color
//and rotate right
else {
if(w.left.color == BLACK) {
w.right.color = BLACK;
w.color = RED;
rotateLeft(w);
w = X.parent.left;
}
//left node is red
w.color = X.parent.color;
X.parent.color = BLACK;
w.left.color = BLACK;
rotateRight(X.parent);
X = root;
}
}
}
//if X became root, make sure it's black
X.color = BLACK;
}
Self-Balanced Binary Search Trees บทท 7
210
เพอใหเหนภาพชดขนเราจะน าขอมล [13, 22, 8, 18, 24, 25] เขาส tree และจะท าการลบ node 8 ออก
ภาพท 7.51 การลบ node 8 ท าใหกฎขอท 5 ถกละเมด
ภาพท 7.52 [ซายสด: การเปลยนสของ node 13 และ node 22] [กลาง: การหมน node 13 ไปทางซาย] [ขวาสด: การเปลยนสของ node 13 และ node 18]
การหมน node ไปทางซาย และทางขวานนกท าคลาย ๆ กบทเราท าใน AVL Tree แตจะตางกนตรงทเราตองดแล node ทเปน parent ดวย ซง code ของกระบวนการทงสองมดงน private void rotateLeft(RBNode<T> X) {
RBNode<T> y = X.right;
X.right = y.left;
if(y.left != nullNode)
y.left.parent = X;
if(y != nullNode)
y.parent = X.parent;
if(X.parent != null) {
if(X == X.parent.left)
X.parent.left = y;
else
X.parent.right = y;
}
13
8
18
22
24
25
13
18
22
24
25
13
18
22
24
25
13
18
22
24
25
13
18
22
24
25
บทท 7 Self-Balanced Binary Search Trees
211
else
root = y;
y.left = X;
if(X != nullNode)
X.parent = y;
}
private void rotateRight(RBNode<T> X) {
RBNode<T> y = X.left;
X.left = y.right;
if(y.right != nullNode)
y.right.parent = X;
if(y != nullNode)
y.parent = X.parent;
if(X.parent != null) {
if(X == X.parent.right)
X.parent.right = y;
else
X.parent.left = y;
}
else
root = y;
y.right = X;
if(X != nullNode)
X.parent = y;
}
เราไดรวบรวม code ทงหมดของ Red-Black Tree มาไวใหอกครงหนง
1: /**
2: Red-black tree
3: */
4:
5: class RBTree<T extends Comparable<? super T>> {
6: private RBNode<T> root;
7: private RBNode<T> nullNode;
8: private final int RED = 0, BLACK = 1;
9:
10: //use nullNode to denote all leaf nodes
11: public RBTree() {
12: //create a null node with black color
13: nullNode = new RBNode<T>(null);
14: nullNode.left = nullNode.right = nullNode;
15: //start with an empty tree
16: root = nullNode;
17: }
18:
19: //insert key into tree
20: public void insert(T key) {
21: RBNode<T> current = root;
22: RBNode<T> parent = null;
23: RBNode<T> newNode;
24:
25: //locating a spot to insert as well as a parent
26: while(current != nullNode) {
27: //no duplicates allowed
28: if(key.equals(current.key))
29: return;
30: //remember parent
31: parent = current;
32: if(key.compareTo(current.key) < 0)
33: current = current.left;
34: else
35: current = current.right;
36: }
37:
38: //creates a new red node
39: newNode = new RBNode<T>(key, parent, nullNode);
Self-Balanced Binary Search Trees บทท 7
212
40: //tree exists, insert as binary search tree
41: if(parent == null)
42: root = newNode; //first time insertion
43: else {
44: if(key.compareTo(parent.key) < 0)
45: parent.left = newNode;
46: else
47: parent.right = newNode;
48: }
49:
50: //restore red-black properties after insertion
51: restoreAfterInsertion(newNode);
52: }
53:
54: //restore tree after insertion
55: private void restoreAfterInsertion(RBNode<T> X) {
56: //perform maintenance only when parent of X is red
57: while(X != root && X.parent.color == RED) {
58: //node was inserted on the left of the tree
59: if(X.parent == X.parent.parent.left) {
60: //y is X's parent sibling (aunt or uncle?)
61: RBNode<T> y = X.parent.parent.right;
62: //aunt or uncle of X is RED and X is an
63: //outside grandchild, do color flip
64: if(y.color == RED) {
65: X.parent.color = BLACK;
66: y.color = BLACK;
67: X.parent.parent.color = RED;
68: X = X.parent.parent; //point X to grand
69: }
70: //aunt or uncle is black we have two cases
71: //either X is an inside grandchild or an outside grandchild
72: //for the first case we rotate left on X, re-color, and
73: //rotate right on X's grandparent for the later,
74: //we re-color and rotate right on X' grandparent
75: else {
76: //X is a right grandchild (inside)
77: if(X == X.parent.right) {
78: X = X.parent;
79: rotateLeft(X);
80: }
81: //X is a left grandchild (outside)
82: //change X's parent's color to black
83: //change X's grandparent's color to red
84: //and perform a right rotation on X's grand
85: X.parent.color = BLACK;
86: X.parent.parent.color = RED;
87: rotateRight(X.parent.parent);
88: }
89: }
90: //node was inserted on the right of the tree
91: //just a mirror-image of the code above
92: else {
93: RBNode<T> y = X.parent.parent.left;
94: //aunt or uncle is red
95: if(y.color == RED) {
96: X.parent.color = BLACK;
97: y.color = BLACK;
98: X.parent.parent.color = RED;
99: X = X.parent.parent;
100: }
101: //aunt or uncle is black
102: else {
103: if(X == X.parent.left) {
104: X = X.parent;
105: rotateRight(X);
106: }
107: X.parent.color = BLACK;
108: X.parent.parent.color = RED;
109: rotateLeft(X.parent.parent);
110: }
111: }
112: }
113: //if somehow during our re-coloring X's color is changed to red,
บทท 7 Self-Balanced Binary Search Trees
213
114: //and X became root, we must change it back to black
115: root.color = BLACK;
116: }
117:
118: //delete a node from tree
119: public void delete(T key) {
120: RBNode<T> X;
121: RBNode<T> successor; //replacement node
122: RBNode<T> deleteNode; //node to be deleted
123: //find node to delete
124: deleteNode = root;
125: while(deleteNode != nullNode) {
126: //found a target node
127: if(key.equals(deleteNode.key))
128: break;
129: //not yet found, keep searching
130: else {
131: if(key.compareTo(deleteNode.key) < 0)
132: deleteNode = deleteNode.left;
133: else
134: deleteNode = deleteNode.right;
135: }
136: }
137: //node to delete is not in a tree, abort
138: if(deleteNode == nullNode) return;
139:
140: //successor has a nullNode as a child (deleteNode is leaf)
141: if(deleteNode.left == nullNode || deleteNode.right == nullNode)
142: successor = deleteNode;
143: //find a successor with a nullNode as a child
144: //locate minimum node on the right
145: else {
146: successor = deleteNode.right;
147: while(successor.left != nullNode)
148: successor = successor.left;
149: }
150:
151: //X is successor's only child
152: if(successor.left != nullNode)
153: X = successor.left;
154: else
155: X = successor.right;
156:
157: //removing successor from parent chain
158: X.parent = successor.parent;
159: if(successor.parent != null) {
160: if(successor == successor.parent.left)
161: successor.parent.left = X;
162: else
163: successor.parent.right = X;
164: }
165: else
166: root = X;
167:
168: //replace the key (keep the color)
169: if(successor != deleteNode)
170: deleteNode.key = successor.key;
171:
172: //restore red-black properties after deletion
173: if(successor.color == BLACK)
174: restoreAfterDelete(X);
175: }
176:
177: //restore tree after deletion
178: private void restoreAfterDelete(RBNode<T> X) {
179: //maintenance needed when black node was deleted
180: while(X != root && X.color == BLACK) {
181: //handle left side
182: if(X == X.parent.left) {
183: //w is a sibling of X (a right child)
184: RBNode<T> w = X.parent.right;
185: //sibling is red
186: if(w.color == RED) {
187: w.color = BLACK;
Self-Balanced Binary Search Trees บทท 7
214
188: X.parent.color = RED;
189: rotateLeft(X.parent);
190: w = X.parent.right;
191: }
192: //both children of w are black, change their parent to red
193: //and reset X to its parent
194: if(w.left.color == BLACK && w.right.color == BLACK) {
195: w.color = RED;
196: X = X.parent;
197: }
198: //they are not both black, re-color and
199: //do double rotations, iff right child of w
200: //is black, otherwise do a left rotation
201: else {
202: //right child of w is black
203: if(w.right.color == BLACK) {
204: w.left.color = BLACK;
205: w.color = RED;
206: rotateRight(w);
207: w = X.parent.right;
208: }
209: //right child of w is red, change w's color to black
210: //change its parent's color to black
211: //change right child's color to black
212: //rotate left on its parent
213: w.color = X.parent.color;
214: X.parent.color = BLACK;
215: w.right.color = BLACK;
216: rotateLeft(X.parent);
217: X = root; //set this new tree
218: }
219: }
220: //handle right side
221: //a mirror-image of code above
222: else {
223: RBNode<T> w = X.parent.left;
224: if(w.color == RED) {
225: w.color = BLACK;
226: X.parent.color = RED;
227: rotateRight(X.parent);
228: w = X.parent.left;
229: }
230: //if left and right nodes are black, their
231: //parent must be red
232: if(w.right.color == BLACK && w.left.color == BLACK) {
233: w.color = RED;
234: X = X.parent;
235: }
236: //w has one red node, two cases to consider
237: //if node on the left is black, we change color
238: //and rotate left on w, then we recolor and
239: //rotate right one more time
240: //if node on the left is red, we change color
241: //and rotate right
242: else {
243: if(w.left.color == BLACK) {
244: w.right.color = BLACK;
245: w.color = RED;
246: rotateLeft(w);
247: w = X.parent.left;
248: }
249: //left node is red
250: w.color = X.parent.color;
251: X.parent.color = BLACK;
252: w.left.color = BLACK;
253: rotateRight(X.parent);
254: X = root;
255: }
256: }
257: }
258: //if X became root, make sure it's black
259: X.color = BLACK;
260: }
261:
บทท 7 Self-Balanced Binary Search Trees
215
262: //rotates left on node X
263: private void rotateLeft(RBNode<T> X) {
264: RBNode<T> y = X.right;
265: X.right = y.left;
266: if(y.left != nullNode)
267: y.left.parent = X;
268:
269: if(y != nullNode)
270: y.parent = X.parent;
271:
272: if(X.parent != null) {
273: if(X == X.parent.left)
274: X.parent.left = y;
275: else
276: X.parent.right = y;
277: }
278: else
279: root = y;
280:
281: y.left = X;
282: if(X != nullNode)
283: X.parent = y;
284: }
285:
286: //rotates right on node X
287: private void rotateRight(RBNode<T> X) {
288: RBNode<T> y = X.left;
289:
290: X.left = y.right;
291: if(y.right != nullNode)
292: y.right.parent = X;
293:
294: if(y != nullNode)
295: y.parent = X.parent;
296:
297: if(X.parent != null) {
298: if(X == X.parent.right)
299: X.parent.right = y;
300: else
301: X.parent.left = y;
302: }
303: else
304: root = y;
305:
306: y.right = X;
307: if(X != nullNode)
308: X.parent = y;
309: }
310:
311: public void printTree() {
312: printTree(root);
313: System.out.println();
314: }
315:
316: private void printTree(RBNode<T> t) {
317: if(t != nullNode) {
318: System.out.print(t.key + "(" + alpha(t.color) + ")" + " ");
319: printTree(t.left);
320: printTree(t.right);
321: }
322: }
323:
324: private char alpha(int value) {
325: return value == RED ? 'R' : 'B';
326: }
327: }
Self-Balanced Binary Search Trees บทท 7
216
7.3 Splay Tree Splay Tree เปน BST อกตวหนงทปรบความสมดลดวยตวเองหลงจากการกระท าตาง ๆ กบ tree เชน การน าขอมลเขาหรอลบ node ออกจาก tree การสราง code ขนมาใชงานของ splay tree นนท าไดงายกวา AVL tree หรอ Red-Black treeผทคดคน Splay tree คอ Daniel Sleator และ Robert Tarjan 7.3.1 โครงสรางของ Splay Tree โครงสรางของ Splay tree นนจะแตกตางกบ search tree ตวอนตรงทมการน าเอากระบวนการทเรยกวา splaying มาใชทกครงทมการเขาหา node ทอยใน tree การ splay หมายถงการทเราน า node ทมการเขาหา2กลบขนไปยงต าแหนง root (move-to-root operation) คณสมบตนท าใหขอมลทเพงถกเขาหาหมาด ๆ งายตอการเขาหาอกครง ภาพท 7.53 แสดง splay tree หลงจากการน าขอมลเขา
ภาพท 7.53 Splay tree หลงจากน าขอมล 18, 8, 23, และ 12 เขา
ภาพท 7.53 แสดง tree ทเกดจากการน าขอมล 18, 8, 23, และ 12 เขา (ตามล าดบ) จะเหนวาขอมลทถกน าเขาครงหลงสดจะอยในต าแหนง root เสมอและ tree กยงคงคณสมบตของ BST ไวทกประการ การลบหรอการเขาหา node ทอยใน tree นนกเหมอนกน (กบการน าเขา) เราตองน า node ทมการเขาหากลบขนไปยง root และกระบวนการดงกลาวเราเรยกวาการ splay ในการ splay นนเราตองค านงถงกรณตาง ๆ อยสามกรณคอ zig-zag, zig-zig, และ zig 7.3.2 การ Splay เมอ node X ไดรบการเขาหาเราตองท าการ splay เพอให X ไปอยในต าแหนง root ซงเปนกระบวนการทท าเปนขนตอน (ตามล าดบ) จนกระทง X อยดานบนสดของ tree เพราะฉะนนการเลอน X ขนดานบนจงขนอยกบต าแหนงของ X, ต าแหนงของ parent ของ X และต าแหนงของ grand parent ของ X (ถาม) 7.3.2.1 Zig-zag หมายถงการท X เปนลกทอยทางดานขวาของ P และ P เปนลกทอยทางดานซายของ G ดงทแสดงในรป 7.54 เราจะเปลยนให P เปนลกทอยทางดานซายของ X และเปลยนให G เปนลกทอยทางดานขวาของ X สวน sub-tree ของแตละ node กยงคงรกษาคณสมบตของ BST ไว เชนเดม
2 ในทนการเขาหาหมายถง การน าขอมลเขา การลบขอมล หรอการดขอมล (look up)
18 8
18
23
18
8
12
8 23
18
บทท 7 Self-Balanced Binary Search Trees
217
ภาพท 7.54 การ splay ในกรณท X เปนลกทางดานขวาของ P และ P เปนลกทางดานซายของ G
7.3.2.2 Zig-zig หมายถงการท X เปนลกทอยทางซายของ P และ P เปนลกทอยทางซายของ G เราตองเปลยน P ใหเปนลกทางขวาของ X และ G ใหเปนลกทางขวาของ P
ภาพท 7.55 การ splay ในกรณท X เปนลกทางซายของ P และ P เปนลกทางซายของ G
เราจะท าการ splay ดวยวธ zig-zig หรอ zig-zag เมอ X ม grand parent เทานน ส าหรบกรณท X ไมม grand parent แตมเพยงแค parent เราจะท าการ splay ดวยวธทเรยกวา zig ซงมขนตอนการท างานดงน
25
19
13
A B
C
D
P
X
G
13
19
25
A
B
C D
G
P
X
1
2
1. หมน node 25 ไปทางขวา (zig)
2. หมน node 19 ไปทางขวา (zig)
25
13
19
A
B C
D
G
P
X
19
13 25
A B C D
G
X
P
1
2
1. หมน node 13 ไปทางซาย (zig) 2. หมน node 25 ไปทางขวา (zag)
Self-Balanced Binary Search Trees บทท 7
218
7.3.2.3 Zig ในกรณท X มแค parent เรากเพยงแคหมน node P เพอให node X มาอยในต าแหนง root
ภาพท 7.56 การ splay เมอ X ไมม grand parent แตมแค parent
ทงสามกรณทกลาวมานนเปนเพยงแคกรณตวอยางทางดานซายเทานน แตโดยความเปนจรงแลว เรายงมกรณทางดานขวาทตองค านงถง ซงโดยทางปฏบตกเปนการกระท าทเหมอนกนแตท าในทางตรงกนขาม (symmetric operation) เราจะทงไวใหผอานวเคราะหถงกรณดงกลาวเอง
ผใหก าเนด Splay tree ไดเสนอใหการ splay นนเปนการ splay จากบนลงลาง (top –down splay tree) ทงนกเนองจากวาการสราง tree แบบนงายกวาการสรางทใชวธการแบบลางขนบน เชนทไดแสดงใหดใน 7.3.2.1 ถง 7.3.2.3 (bottom-up splay tree) 7.3.3 การ splay จากบนลงลาง (Top-down) การสราง tree ดวยการ splay จากบนลงลางนนมวธการคราว ๆ คอ ในขณะทเราก าลงลงส tree
เพอเขาหา node X เราตองท าการยาย node ทอยระหวางทางของการคนหาออกจาก tree (ทงตว node และ sub-tree ของ node ดวย) พรอมกนนเราตองท าการหมน node (zig, zig-zig, หรอ zig-zag) เพอรกษาคณสมบตของ splay tree ไว ในการ implement นนเราจะใช tree ทงหมดสามตว คอ L, R, และ tree ทอยในเงอนไข (middle tree) เมอเรามาถง node X เรารวา X เปน root ของ sub-tree นนเราจะใช L เปนตวเกบ node ทมคานอยกวา X และจะใช R เกบ node ทมคามากกวา X
เมอเรมตนนน X เปน root ของ tree และ L กบ R เปน tree ทไมม node อยเลย (empty) เมอเราลงไปใน tree ครงละสองระดบเราจะเจอกบ node คหนงทเราตองท าการยาย ซงกข นอยกบวา node ทงสองนอยกวาหรอมากกวา X เราจะยาย node และ sub-tree ของมนเขาส L หรอ R รวมไปถง sub-tree ทไมไดอยใน path ทใชเขาหา X เพราะฉะนน node ปจจบนท algorithm ก าลงตรวจสอบอยจงเปน root ของ tree ทอยตรงกลางเสมอ และเมอเราเขาถง X แลวเรากท าการเชอม node ทอยใน L และ R เขากบ tree ทอยตรงกลาง (ท าให X เปน root ของ tree) ขนตอนทเหลอจงเปนการน า node เขาส L และ R พรอมกบการเชอมของ L และ R เขาส tree
เราจะแสดงกระบวนการดงกลาวดวยภาพตอไปน
19
13
A
B C
X
P
19
13
A B
C
P
X
บทท 7 Self-Balanced Binary Search Trees
219
ภาพท 7.57 Top-down splay ดวยการ zig
ระหวางการ splay นนถาการหมนของเราอยในขายของ zig เราจะก าหนดให tree ทม Y เปน root นนเปน root ใหมของ tree ทอยตรงกลาง พรอมกนนเราจะก าหนดให X และ sub-tree ของ X เปนลกทอยทางซายของคาทนอยทสดของ R กระบวนการนท าให X เปนคาทนอยทสด (จากภาพท 7.57 เราท าใหลกทางซายของ X เปน null) ส าหรบกรณทการหมนอยในขายของ zig-zig นนเราตองท าการหมน node X และ Y เพมข นมาจากกระบวนการทเราท าในการ zig (ดภาพท 7.58 ประกอบ)
ภาพท 7.58 กระบวนการ zig-zig และการหมน node X และ Y
และในกรณของการ zig-zag นนเราจะก าหนดให Y เปน root ของ tree ทอยตรงกลาง (โดยปกตเราจะให Z เปนแตเนองจากวาเราไมตองท าการหมน node – เราเรยกวธหมนแบบนวา simplified zig-zag ผอานอาจสงสยวาท าไม สาเหตกเพราะวาเราตองการใหการเขยน code นนงายขน เพราะวาการ zig-zag กลายมาเปน zig) และเราจะเชอม X และ Z ดงทแสดงในภาพท 7.59
L R
X
A B
Y
L R
X
A
B
Y
zig
L
X
A
B
Z
Y
C
R R
X
A
B
Z
Y
C
L
zig-zig
Self-Balanced Binary Search Trees บทท 7
220
ภาพท 7.59 กระบวนการ zig-zag แบบ simplified
ขนตอนทเหลออยกคอการน าเอาทง L, R, และ tree ทอยตรงกลางมาประกอบกนใหเปน tree ตวเดยวดงภาพท 7.60
ภาพท 7.60 ขนตอนสดทายของการ splay จากบนลงลาง
เชนเดยวกบทเราไดพดถง AVL tree และ Red-Black tree เราไมไดแสดงกระบวนการ splay ในทศตรงกนขาม (symmetric operation) ผอานควรศกษาและวาดภาพดงกลาวเอง เพอใหเหนภาพชดเจนยงข นเราจะแสดงกระบวนการทงหมดอกครงหนงดวยขอมลทมอยใน tree ทเหนนโดยเราจะท าการคนหา node 24
L R
X
A
B
Z
Y
C X
C
A
B
Z
Y
R L
zig-zag
X
A B
L R
X
R L
B A
บทท 7 Self-Balanced Binary Search Trees
221
ภาพท 7.61 Splay tree ตวอยาง เพอการคนหา node 24
หลงจากทเราตรวจสอบและเดนเขาหา tree ครงละสองระดบเรากมาถง node 26 (เปนขนตอนของการ zig-zag: 18 31 26) เรากก าหนดให node 31 เปน root ของ tree ทอยตรงกลาง (ถาเปน zig-zag ตามปกต node 26 จะเปน root แตเราจะใชวธของ simplified zig-zagแทน) พรอมกบท าการเชอม node 18 และ sub-tree ของ node 18 เขากบ tree L เรากจะได tree ดงทแสดงในภาพท 7.62
ภาพท 7.62 simplified zig-zag
เมอเราเดนเขาหา tree ตอไปเรากจะเจอกรณของการ zig-zig (31 26 21) เรากน าเอา node 21 ขนมาเปน root ของ tree ตรงกลางพรอมกบท าการเชอม node เหลานเขากบ tree R หลงจากการหมนระหวาง node 26 และ node 31 ดงทแสดงในภาพท 7.63
18
L R
13
5 15
31
26 36
21 30
24 19
Empty Empty
R
18
13
5 15
31
26 36
21 30
24 19
Left Middle
Empty
Self-Balanced Binary Search Trees บทท 7
222
ภาพท 7.63 การ zig-zig และการหมน node
ภาพท 7.64 การ zig และการเชอม node 19 และ 21 เขาส tree ทางซาย
เมอเราท าการ zig จาก node 21 กบ node 24 เรากได node 24 เปน root สวน node 19 และ
node 21 กไปเชอมอยกบ node 18 ดงทแสดงในภาพท 7.63 ขนตอนสดทายทเราตองท ากคอการรวมเอา tree ทงสามเขาดวยกน ซงกท าใหเราได tree ทม 24 เปน root node ดงทแสดงในภาพท 7.64
ภาพท 7.65 Tree หลงจากการคนหา node 24
การคนหาทไมเจอ node ทตองการกท าให node ทก าลงถกตรวจสอบ ณ เวลานนยายขนไปเปน root node เชนเดยวกน เชนสมมตวาเราตองการคนหา node 22 ของ tree ในภาพท 7.65 เราก
18
13
5 15
21
24 19 31
26
36 30
Left Middle Right
24
31
26
36 30
18
13
5 15 21
19
Left Middle Right
18
13
5 15 21
19
24
31
26
36 30
บทท 7 Self-Balanced Binary Search Trees
223
จะได tree ทม node 21 เปน root node แต sub-tree ทอยทางดานขวากจะไมเหมอนเดม (node 24 จะมาเชอกบ node 26) Code ของการ splay แบบบนลงลางมดงน private SplayNode<T> splay(T key, SplayNode<T> t) {
SplayNode<T> L; //left tree
SplayNode<T> R; //right tree
header.left = header.right = nullNode;
L = R = header;
nullNode.key = key;
while(true) {
//descend two levels on the left
if(key.compareTo(t.key) < 0) {
if(key.compareTo(t.left.key) < 0) {
t = rotateRight(t);
}
if(t.left == nullNode)
break;
//move nodes to the right tree
R.left = t;
R = t;
t = t.left;
}
//descend two levels on the right
else if(key.compareTo(t.key) > 0) {
if(key.compareTo(t.right.key) > 0) {
t = rotateLeft(t);
}
if(t.right == nullNode)
break;
//move nodes to the left tree
L.right = t;
L = t;
t = t.right;
}
else break;
}
//reattach left and right trees
L.right = t.left;
R.left = t.right;
t.left = header.right;
t.right = header.left;
return t;
}
7.3.4 การน าขอมลเขา การน าขอมลเขาส Splay tree นนเมอเราสราง node ใหมแลวเรากตองตรวจสอบดวา tree ของเรามขอมลอยหรอไม ถาไมมเรากสราง tree ทมเพยงแค node ใหมนแตถามเราตองท าการ splay ดวยขอมลทเราตองการน าเขา ซงจะท าให tree ของเราม root ตวใหม (เราตองไมลมวาการ splay จะท าให tree เปลยนรป) ถาขอมลใน root ใหมมคานอยกวาขอมลใน node ใหมเรากจะน าเอา root ใหมนพรอมกบ sub-tree ทอยทางดานซายมาเชอตอกบลกทางซายของ node
ใหม แตถาขอมลของ root ใหมมคามากกวาเรากจะน าเอา root ใหมและ sub-tree ทางขวามาเชอมกบลกทางขวาของ node ใหม 7.3.5 การดงขอมลออก การลบ node ออกจาก tree เรมดวยการคนหา node ทมขอมลตามทตองการดวยการ splay ซงเรารวาถาหาเจอ node นจะเปน root ซงเราตองแทนคาของ node นดวยการคนหา node ทม
ขอมลนอยทสดทางดานขวา (หรอผอานอาจเปลยนใหเปน node ทมขอมลมากทสดทางซายกได) ซงกท าไดดวยการ splay เชนเคยและการ splay นจะการนตวา node นเปน node ทมขอมลนอยทสด (จรง ๆ แลวกคอ node ทมคา (มาก) ตอจาก node ทตองการลบออกนนเอง)
Self-Balanced Binary Search Trees บทท 7
224
7.3.6 การคนหาขอมล การคนหาขอมลนนเรากเพยงแตเรยกใชกระบวนการ splay ดวยขอมลทตองการคนหา ซงกจะท าใหขอมลนมาอยในต าแหนง root ถามอยจรงใน tree เรากเพยงแตตรวจสอบขอมลตวนเทานนเอง เราไมไดแสดง code ของการน าขอมลเขา การดงขอมลออก หรอการคนหาทงนเพราะวาเราได น าเอา code ทงหมดมาแสดงไว (ทเดยว) ดงทเหนน
1: /**
2: Top-down Splay Tree
3: */
4:
5: public class SplayTree<T extends Comparable<? super T>> {
6: private SplayNode<T> root;
7: private SplayNode<T> nullNode;
8: private SplayNode<T> newNode = null;
9: private SplayNode<T> header = new SplayNode<T>(null);
10:
11: //use nullNode to denote all leaf nodes
12: public SplayTree() {
13: nullNode = new SplayNode<T>(null);
14: nullNode.left = nullNode.right = nullNode;
15: //start with an empty tree
16: root = nullNode;
17: }
18:
19: //insert key into tree
20: public void insert(T key) {
21: //create new node with given key
22: if(newNode == null)
23: newNode = new SplayNode<T>(null);
24: newNode.key = key;
25:
26: //first time insertion, make one node tree
27: if(root == nullNode) {
28: newNode.left = newNode.right = nullNode;
29: root = newNode;
30: }
31: //tree exists
32: else {
33: //move candidate node to root
34: root = splay(key, root);
35:
36: //insert new node to this tree
37: if(key.compareTo(root.key) < 0) {
38: newNode.left = root.left;
39: newNode.right = root;
40: root.left = nullNode;
41: root = newNode;
42: }
43: else if(key.compareTo(root.key) > 0) {
44: newNode.right = root.right;
45: newNode.left = root;
46: root.right = nullNode;
47: root = newNode;
48: }
49: else return; //duplicate key - do nothing
50: }
51: newNode = null;
52: }
53:
54: //delete node with a given key
55: public void delete(T key) {
56: SplayNode<T> t;
57:
58: //locate node with a given key,
59: //if found, it's at root
60: root = splay(key, root);
61:
62: //do nothing if key doesn't exist
บทท 7 Self-Balanced Binary Search Trees
225
63: if(root.key.compareTo(key) != 0)
64: return;
65:
66: //delete root node
67: if(root.left == nullNode)
68: t = root.right;
69: //locate smallest key on the right,
70: //splay it to root and connect left child
71: else {
72: t = root.right;
73: t = splay(key, t);
74: t.left = root.left;
75: }
76: root = t;
77: }
78:
79: //search for a node with a given key
80: public boolean search(T key) {
81: //node doesn't exist
82: if(root == nullNode)
83: return false;
84:
85: //if found, it's at root
86: root = splay(key, root);
87:
88: return root.key.compareTo(key) == 0;
89: }
90:
91: //top-down splaying
92: private SplayNode<T> splay(T key, SplayNode<T> t) {
93: SplayNode<T> L; //left tree
94: SplayNode<T> R; //right tree
95:
96: header.left = header.right = nullNode;
97: L = R = header;
98: nullNode.key = key;
99:
100: while(true) {
101: //descend two levels on the left
102: if(key.compareTo(t.key) < 0) {
103: if(key.compareTo(t.left.key) < 0) {
104: t = rotateRight(t);
105: }
106: if(t.left == nullNode)
107: break;
108: //move nodes to the right tree
109: R.left = t;
110: R = t;
111: t = t.left;
112: }
113: //descend two levels on the right
114: else if(key.compareTo(t.key) > 0) {
115: if(key.compareTo(t.right.key) > 0) {
116: t = rotateLeft(t);
117: }
118: if(t.right == nullNode)
119: break;
120: //move nodes to the left tree
121: L.right = t;
122: L = t;
123: t = t.right;
124: }
125: else break;
126: }
127: //reattach left and right trees
128: L.right = t.left;
129: R.left = t.right;
130: t.left = header.right;
131: t.right = header.left;
132:
133: return t;
134: }
135:
136: //rotate right once (same as AVL)
Self-Balanced Binary Search Trees บทท 7
226
137: private SplayNode<T> rotateRight(SplayNode<T> X) {
138: SplayNode<T> temp = X.left;
139: X.left = temp.right;
140: temp.right = X;
141:
142: return temp;
143: }
144:
145: //rotate left once (same as AVL)
146: private SplayNode<T> rotateLeft(SplayNode<T> X) {
147: SplayNode<T> temp = X.right;
148: X.right = temp.left;
149: temp.left = X;
150:
151: return temp;
152: }
153:
154: //pre-order traversal
155: private void pretraversal(SplayNode<T> node, StringBuffer buf) {
156: if(node != nullNode) {
157: buf.append(node.key + ", ");
158: pretraversal(node.left, buf);
159: pretraversal(node.right, buf);
160: }
161: }
162:
163: public String toString() {
164: StringBuffer buf = new StringBuffer();
165: buf.append("(");
166: pretraversal(root, buf);
167: buf.delete(buf.length()-2, buf.length());
168: buf.append(")");
169:
170: return new String(buf);
171: }
172: }
สวน code ของ SplayNode กมดงน
1: /**
2: Splay node for (Top-down) Splay Tree
3: */
4:
5: public class SplayNode<T> {
6: T key; //key to insert
7: SplayNode<T> left; //left child
8: SplayNode<T> right; //right child
9:
10: //default constructor
11: public SplayNode(T key) {
12: this.key = key;
13: this.left = this.right = null;
14: }
15: }
ผอานควรทดสอบการท างานของ splay tree ดวยการใชกลมขอมลทตางกนเพอใหเกดความเขาใจการท างานของการน าขอมลเขา การลบขอมล และการคนหาขอมลไดดย งข น สรป ในบทนเราไดพดถงโครงสรางของ AVL Tree, Red-Black tree, และ Splay Tree ซงทงสามตวเปน tree ทเมอมการเขาหา node ใด ๆ กจะปรบความสมดลโดยอตโนมต เราไดพดถง การน าขอมลเขา การดงขอมลออก และการคนหาขอมล การปรบความสมดลของ AVL Tree เราใชคาความแตกตางของความสงของ tree ทางซายและทางขวาเปนตวก าหนด ถาสงเกนกวาหนงเราจงจะปรบ tree การปรบความสมดลของ Red-Black Tree เราใชสของ node และกฎขอทหาเปนตวก าหนด นนกคอเราไมสามารถม node สองตวตดกนเปนสแดง และจ านวนของ black node จาก root ไปยง leaf node ใด ๆ ตองมเทากน การปรบความสมดลของ Splay Tree เราใช
ขอมลทน าเขาครงลาสด หรอ node ทมการเขาหาเปนตวก าหนดซงท าใหขอมลเหลานอยไมหางจาก root node เทาใดนก โดยรวมแลวเราไดพดถง
บทท 7 Self-Balanced Binary Search Trees
227
ขอก าหนดของ AVL Tree, Red-Black Tree, และ Splay Tree การน าขอมลเขา และการดงขอมลออก การคนหาขอมล การปรบความสมดลของ AVL Tree, Red-Black Tree, และ Splay Tree
แบบฝกหด 1. จาก AVL Tree ทก าหนดให จงเขยนภาพของ Tree ทเกดข นหลงจากการลบ node
ดงตอไปน (ใหใช AVL Tree ทก าหนดใหทกครงกอนการลบ node)
ก. ลบ node k ง. ลบ node a ช. ลบ node h ข. ลบ node c จ. ลบ node g ค. ลบ node j ฉ. ลบ node m
2. จงเขยน method ทนบจ านวนของ node ในแตระดบทมอยใน BST เชน จากรปในขอ 1
ระดบ 0 มหนง node ระดบหนงม 2 node ระดบสองม 4 node ระดบสามม 5 node และระดบสม 1 node ใหเขยนโปรแกรมทดสอบ
3. จงแสดงภาพของ AVL Tree เมอมการน าขอมลเหลานเขา 5, 16, 22, 45, 2, 10, 18, 30,
50, 12, 1 4. จงออกแบบ class ทใชโครงสรางของ AVL Tree เปนตวเกบขอมลโดยมเงอนไขวา ในแต
ละ node ของ AVL Tree จะตองเปนการเปนค เชน (key, data) โดยการน าเขาและดงออกจะใช key เปนตวก าหนด สวน data เปนขอมลทเกบอยภายในหนวยความจ า ณ key ทก าหนดให (ด class TreeMap ของ Java ประกอบ)
5. จงเขยนโปรแกรมทดสอบการท างานของ AVL Tree โดยก าหนดใหน าขอมลเขาส AVL Tree เปนจ านวนเทากบ 10000 ตว (Integer หรอ String) หลงจากนนใหท าการจบเวลาการคนหาขอมลทอยใน node ทางดานซายสด และ node ทอยทางดานขวาสด
6. จงเขยนโปรแกรมทรบ Binary Search Tree เปนขอมลน าเขา และท าการเปลยน BST นให
เปน AVL Tree โดยก าหนดใหม method changeTree() เปนตวจดการ การท างานดงกลาว parameter ทตองสงไปให changeTree() คอ root node ของ BST และสงท changeTree() สงกลบออกมาคอ AVL Tree
7. ท าตรงกนขามกบโจทยในขอ 6 โดยเปลยนขอมลทน าเขาเปน AVL Tree และขอมลทได
กลบออกมาเปน BST
H
A C
D
E
B J F
L
I K G
M
Self-Balanced Binary Search Trees บทท 7
228
8. จงเขยน method เพอใชในการนบ node ทเปน parent node ใน Red-Black Tree 9. จงเขยนรปของ Red-Black Tree ทมการน าขอมล A ถง K เขาพรอมทงวเคราะหถง
เหตการณทเกดข น 10. จงสราง Red-Black Tree สองตนจากขอมลแบบสม (random) 32 ตวพรอมทงวาดภาพ
ของ Tree ทเกดข น ใหท าการเปรยบเทยบกบ BST ทสรางขนจากขอมลเดยวกน 11. จงเขยนโปรแกรมทนบจ านวนเปอรเซนตของ node สด าใน Red-Black Tree ใหท าการ
ทดสอบดวยการน าขอมลแบบสมเขาจ านวน N ตวโดยเรมทดสอบท N = 103, 104, 105, และ 106
12. จงเขยน method ส าหรบการคนหา node ทก าหนดใหใน Red-Black Tree 13. การลบ node ออกจาก Red-Black Tree ของเราใชการแทนท node ดวย in-order
successor หรอทเรยกวาการแทนทดวย node ทมคานอยทสดทางขวา จงเขยน method ทแทนท node ทถกลบดวย node ทมคามากทสดทางซาย
14. จาก Red-black Tree ทใหจงแสดงขนตอนทเกดข นเมอลบ node 32 และ node 34
(ตามล าดบ)
15. จงแสดงภาพของ Red-Black tree หลงจากน าขอมลตงแต 'A' ไปจนถง 'M' เขาส tree และเมอลบ 'C', 'H', 'L' และ 'M' ออกจาก tree
16. จากโจทยในขอ 15 จงใชขอมลเดยวกนแสดงภาพของ Splay tree 17. การลบ node ออกจาก Splay tree ในตวอยางใช node ทมคานอยทสดทางขวามาแทน
node ทถกลบออก จงเขยน method delete() ทใช node ทมคามากทสดทางซายมาแทน
18. จงเขยน method ส าหรบการคนหา node ทมคานอยทสดใน Splay tree ดวยการใช
recursion 19. จงเขยน method ส าหรบการคนหา node ทมคามากทสดใน Splay tree ดวยการใช loop
34
28
32 17
19
60
54 65