Professional Documents
Culture Documents
1. CÂY M NHÁNH
a) Định nghĩa
Cây m-nhánh là một cây tìm kiếm có những tính chất sau.
- Mỗi nút có tối thiểu 1 khóa, tối đa (m – 1) khóa có giá trị phân biệt.
- Các khóa trong mỗi nút được sắp xếp theo thứ tự tăng dần
- Mỗi nút có m-1 khóa {v0, ..., vm-2 } thì sẽ có m cây con {T0, ..., Tm-1}, các cây
con có thể rỗng
+ Cây con đầu T0 sẽ chứa các khóa v trong khoảng v ∈ (−∞, v0) (1)
+ Cây con cuối Tm-1 sẽ chứa các khóa v trong khoảng v ∈ (vm-2 , ∞) (2)
+ Cây con Ti , i = 1, ..,m-1 sẽ chứa các khóa v trong khoảng v ∈ (vi-1 , vi) (3)
+ Mỗi khóa vi sẽ có cây con trái là Ti-1 và cây con phải Ti
struct BTree {
Node* root;
};
void Initialize(BTree& b) {
b.root = NULL;
}
- Ví dụ minh họa :
b.6) Xóa một khóa khỏi cây
Cách hoạt động :
- Xóa khóa ở nút lá :
+ Nếu sau khi xóa khóa, số khóa trong nút >= (M-1)/2 thì dừng
+ Nếu sau khi xóa khóa, nút có ít hơn (M-1)/2 khóa
Nếu nút anh/em có > (M-1)/2 khóa thì mượn khóa từ nút anh/em
Nếu nút anh/em có <= (M-1)/2 khóa thì merge node và đưa khóa từ nút
cha xuống. Nếu nút cha thiếu khóa thì lặp lại quá trình này.
- Xóa khóa ở nút không phải là nút lá
+ Áp dụng phương pháp “tìm phần tử thay thế” và xóa khóa ở nút lá.
Code :
// kiểm tra xem nút đó có phải nút lá không
bool isLeaf(Node* p) {
if (p == NULL)return false;
for (int i = 0; i <= p->count; i++) {
if (p->child[i] != NULL)return false;
}
return true;
}
// kiểm tra xem một giá trị có tồn tại trong nút không
bool isExistNode(Node* p,int value) {
if (p == NULL) return false;
for (int i = 0; i < p->count; i++) {
if (p->value[i] == value) return true;
}
return false;
}
int findSibling(Node* del, Node* parent, int value, Node*& siblingLeft, Node*&
siblingRight) {
int id = 0;
// tìm kiếm vị trí của nút con của parent chứa value
while (id <= parent->count) {
if (isExistNode(parent->child[id], value)) break;
id++;
}
// Nếu nút con đấy không nằm ngoài cùng bên trái thì :
if (id > 0) {
siblingLeft = parent->child[id - 1]; // có anh em trái
if (id < parent->count) { // nếu nút con đấy không nằm ngoài cùng
bên phải thì có anh em phải
siblingRight = parent->child[id + 1];
}
}
else { // Nếu nút con đấy nằm ngoài cùng bên trái thì nó chỉ có anh em
bên phải là :
siblingRight = parent->child[id + 1];
}
return id;
}
// hàm hợp nhất lại với anh em nếu anh em không dư khóa
void merge(Node*& root, Node*& del, Node*& parent, Node*& siblingLeft, Node*&
siblingRight, int &pos,int id) {
clear(del, pos); // xóa khóa đấy
if (siblingLeft != NULL) { // nếu có anh em trái thì :
siblingLeft->value[siblingLeft->count] = parent->value[id - 1];
// đẩy khóa cha xuống nút anh em trái
siblingLeft->count++; // cập nhật lại số lượng khóa của anh em
trái
// hợp nhất lại với anh em bằng cách dồn hết qua bên phải của anh
em trái
for (int i = 0; i < del->count; i++) {
siblingLeft->value[siblingLeft->count] = del->value[i];
siblingLeft->count++;
}
// đẩy các nút con từ thứ id của parent qua bên trái một nút
for (int j = id; j < parent->count; j++) {
parent->child[j] = parent->child[j + 1];
}
// cho nút con ngoài cùng bên phải của parent bằng NULL
parent->child[parent->count] = NULL;
delete del;
pos = id - 1; //lưu lại vị trí của khóa cha
}
else { // nếu có anh em phải thì :
del->value[del->count] = parent->value[id]; // đẩy khóa cha xuống
nút del
del->count++; // cập nhật lại số lượng khóa của nút del
int z = del->count; // giữ số lượng khóa của nút del trước khi
hợp nhất với anh em
// hợp nhất với anh em phải bằng cách dồn hết khóa của anh em
phải vào
for (int i = 0; i < siblingRight->count; i++) {
del->value[del->count] = siblingRight->value[i];
del->count++;
}
// nếu nút cha chính là nút gốc thì :
if (parent == root) {
// dồn các nút con của anh em phải qua
for (int k = 0; k <= siblingRight->count; k++) {
del->child[z + k] = siblingRight->child[k];
}
parent = NULL;
Node* tmp = root;
root = del; // cập nhật lại nút gốc chính là nút del sau
khi hợp nhất
delete tmp; // xóa nút gốc cũ
} // nếu nút cha không phải nút gốc thì :
else {
// đẩy các nút con từ thứ id+1 của parent qua bên trái một
nút
for (int j = id + 1; j < parent->count; j++) {
parent->child[j] = parent->child[j + 1];
}
parent->child[parent->count] = NULL;
delete siblingRight; // xóa nút anh em phải
pos = id; // lưu lại vị trí của khóa cha
}
}
}
45,56 có trong cây nên sẽ in ra tìm thấy còn 12,34 không có trong cây nên sẽ in ra
không tìm thấy
Cây sau khi xóa 4,5,7,3,19 :