Định nghĩa và tính chấtCây đỏ đen là một cây nhị phân tìm kiếm BST tuân thủ các quy tắc sau: Mọi node phải là đỏ hoặc đen.Node gốc và các nốt lá NIL phải luôn luôn đen.Nếu một node là đỏ
Phép tìm ki m ếm
Thực hiện tương tự như phép tìm kiếm trong cây nhị phân theo 2 cách: Đệ quy hoặc Không đệ quy. Đệ quy:
RBNode *search (RBNode * root, int key) { if (root =null) return null; if (root ->info==key) return root; if (root->info right, key) else return search (root -> left, key);
RBNode * search (RBnode * root, int key) {
RBnode * p=root; while (p!=null){ if (p->info==key)return p; if (p->inforight; else p=p->left;
Phép quay
Thực ra quay không có nghĩa là các node bị quay mà để chỉ sự thay đổi quan hệ giữa chúng Một node được chọn làm "đỉnh" của phép quay Nếu chúng ta đang thực hiện một phép quay qua phải, node "đỉnh" này sẽ di chuyển xuống dưới và về bên phải, vào vị trí của node con bên phải của nó Node con bên trái sẽ đi lên để chiếm lấy vị trí của nó.
Phải đảm bảo trong phép quay phải, node ở đỉnh phải có node con trái Nếu không chẳng có gì để quay vào điểm đỉnh Tương tự, nếu làm phép quay trái, node ở đỉnh phải có node con phải.
Thao tác thêm node
Việc thực hiện chèn một node N vào cây đỏ đen sẽ được thực hiện theo các bước sau đây:
B1: Gán màu của Node cần chèn là màu đỏ.
B2: Thực hiện chèn một node vào cây đỏ đen giống như thao tác chèn trên cây nhị phân tìm kiếm (nếu cây rỗng thêm N thì N là gốc, cây khác rỗng → kiểm tra xem lớn hay nhỏ hơn gốc: nhỏ → trái, lớn → phải).
B3: Điều chỉnh lại nếu thêm mới vi phạm các qui định của cây đỏ đen.
Hình 5 Sơ đồ phả hệ
N: Node vừa được thêm vào cây.
U: Node chú bác của N (node cùng cha với P).
G: Node cha của nút P (node ông của P). b, Các trường hợp xảy ra nếu thêm node
TH1: N được chèn vào vị trí node gốc của cây N → N là gốc Nếu N không là gốc thì: N có P Khi đó dẫn đến TH2.
TH2: P là node đen, nếu P không đen thì dẫn đến TH3.
TH3: Cả P và U đều đỏ.
TH4: P là node đỏ, U là node đen
Hình 6 Các trường hợp thêm node
TH1: N được chèn vào vị trí node gốc của cây
Nút chèn là nút đỏ → vi phạm yêu cầu: nút gốc phải là nút đen → đổi màu nút
Hình 7 Trường hợp N được chèn là node gốc
Hình 8 P là node đen
→ Không vi phạm định nghĩa cây đỏ đen.
→ Không vi phạm tính chất của cây đỏ đen
TH3: Cả P và U đều đỏ
Hình 9 Cả P và U đều đỏ
→ Vi phạm yêu cầu: cha của nút đỏ phải là nút đen.
→ Chuyển P và U thành node đen (cả 2 con node đỏ phải là đen)
(đường đi của G và U chỉ có 1 đen → tăng 2 nút đen → dư 1 đen).
(có thể dẫn đến dây chuyền vi phạm → xét tiếp)
VD: Tạo cây đỏ đen từ dãy số sau: 50, 75, 25, 80, 100, 110, 105
TH4: P là node đỏ, U là node đen
P là con trái của G, N là con trái của P (Left - Left).
P là con phải của G, N là con phải của P (Right - Right).
P là con trái của G, N là con phải của P (Left - Right).
P là con phải của G, N là con trái của P (Right - Left).
Hình 10 Cả P và U đều đỏ
Hình 11 Minh họa thêm node
TH4.1 P là con trái của G, N là con trái của P (Left – Left ): P đỏ, U đen, P trái G,
Hình 12 Trường hợp Left–Left
→ Vi phạm yêu cầu: cha nút đỏ phải là nút đen.
→ Đổi màu node G và P: P thành đen, G thành đỏ (đảm bảo chiều cao đen)
TH4.2 P là con phải của G, N là con phải của P(Right - Right): P đỏ, U đen, P phải
Hình 14 Trường hợp Right–Right
→ Vi phạm yêu cầu: cha nút đỏ phải là nút đen
→ Đổi màu node G và P: P thành đen, G thành đỏ (đảm bảo chiều cao đen)
Hình 13 Trường hợp Left–Left
Hình 15 Trường hợp Right–Right
TH4.3 P là con trái của G, N là con phải của P(Left - Right): P đỏ, U đen, P trái G,
→ Vi phạm yêu cầu: cha nút đỏ phải là nút đen.
Hình 16 Trường hợp Left–Right
→ Đổi màu node G và N: N thành đen, G thành đỏ (đảm bảo chiều cao đen)
TH4.4 P là con phải của G, N là con trái của P (Right - Left): P đỏ, U đen, P phải
Hình 18 Trường hợp Right–Left
→ Vi phạm yêu cầu: cha nút đỏ phải là nút đen.
Hình 17 Trường hợp Left–Right
Hình 19 Trường hợp Right–Left
→ Đổi màu node G và N: N thành đen, G thành đỏ (đảm bảo chiều cao đen)
Hình 20 Trường hợp Right–Left
VD: Tạo cây đỏ đen từ dãy số sau: 50, 75, 25, 80, 100, 110, 105 (tiếp tục với 100)
Thao tác xóa node trên cây đ đen ỏ đen
B1: Tìm kiếm node cần xóa, nếu không tìm thấy thì dừng.
B2: Nếu tìm thấy, ta tiến hành xóa.
Node cần xóa là nút lá.
Node cần xóa là nút 1 con: Nối con trỏ trỏ vào nút cần xóa tới thẳng con của nút cần xóa.
Node cần xóa là nút 2 con (tìm node thay thế, thường là nút lớn nhất của con bên trái).
B3: Điều chỉnh lại nếu vi phạm các quy định của cây đỏ đen. b, Các TH có thể xảy ra nếu xóa node X của cây đỏ đen
TH1: Node cần xóa X là node đỏ hoặc node gốc
Việc xóa node X sẽ không ảnh hưởng tới đặc điểm và tính chất của cây đỏ đen nên không cần điều chỉnh.
Hình 21 Minh họa thêm node
Nếu xóa node bên phải thì duyệt đến khi node con bên trai là lá thì lấy lá thế chỗ cho node xóa.
TH2: Node cần xóa X là node đen:
TH2.1 Node con của X là nút đỏ (có 1 con):
Sau khi xóa X thì nối P với con của X (Y) Điều chỉnh màu của node con của X thành màu đen
TH2.2 Hai con của X (kể cả node NIL) đều là node đen:
Gọi S là node anh em của X: Có 4 trường hợp xảy ra:
S màu đen và 2 con của S màu đen
Hình 22 Xóa node gốc Hình 23 Xóa node đỏ
Hình 24 Node con của X là nút đỏ (có 1 con)
S màu đen và con phải của S màu đỏ
S màu đen và con trái của S màu đỏ, con phải S màu đen
Hình 25 Hai con của X (kể cả node NIL) đều là node đen
Tất cả các đường đi qua nút X sẽ bị ít hơn 1 nút đen
→ Y được gọi là nút 2 đen (đen kép)
TH2 2.1 S màu đen và 2 con của S màu đen: Để đảm bảo định nghĩa, tính chất của cây đỏ đen Đổi nút S sang màu đỏ, Đổi nút P sang màu đen
TH2 2.2 S màu đen và con phải của S màu đỏ (con trái màu gì cũng được):
Sau đó tiến hành đảo màu của P và S: P màu gì S màu đó, Z đổi màu đen
Hình 26 S đen và 2 con của S màu đen
Hình 27 S màu đen và con phải của S màu đỏ
TH2 2.3 S màu đen và con trái của S màu đỏ, con phải của S màu đen:
Ta tiến hành quay phải tại S Đổi màu Z và S
Hình 28 Minh họa xóa node
Hình 29 S màu đen và con trái của S màu đỏ, con phải S màu đen
Sau khi đổi màu, trở lại trường hợp trước đó: S màu đen và con phải của S màu đỏ
Quay trái tại P và tiến hành đảo màu
Tiến hành quay trái tại P
Hình 30 S màu đen và con trái của S màu đỏ, con phải S màu đen
Hình 31 S màu đỏ Đổi màu P sang đỏ, S sang đen:
Y vẫn là nút đen kép, tùy từng cấu trúc của cây xem xem nó rơi vào trường hợp nào trong các TH rồi tiến hành tương tự.
Khi xóa 50, tiến hành đổi chỗ 25 và 50
Hình 33 Minh họa xóa node
Hình 34 Minh họa xóa node
PHẦN 3: THUẬT TOÁN CÀI ĐẶT
Thêm node
#include using namespace std;
#define COUNT 10 struct Node { int data;
// Gán x->right vào left root root->left = x->right; if (x->right != NULL) x->right->parent = root;
// Gán root vào x.right x->right = root; root->parent = x;
// Gán y->left vào right root root->right = y->left; if (y->left != NULL) y->left->parent = root;
// Gán root vào y.left y->left = root; root->parent = y;
Node* Root; bool ll = false; bool rr = false; bool lr = false; bool rl = false;
Node* insertHelp(Node* root, int key) {
// f đúng khi có xung đột RED RED bool f = false; if (root == NULL) { return new Node{ key, NULL, NULL, NULL, 1 }; // RED Node
} else if (key < root->data) { root->left = insertHelp(root->left, key); root->left->parent = root;
// root = X->parent if (Root != root) { if (root->color == root->left->color == 1) f = true;
} else { root->right = insertHelp(root->right, key); root->right->parent = root;
// root = X->parent if (Root != root) { if (root->color == root->right->color == 1) f = true;
// *** Khi này (ll, lr, rr, rl = false) nên chưa xử lí liền // *** Sau khi thoát 1 vòng đệ quy thì: root = X->parent-
// *** Tức là Node ông, lúc này ta quay Node ông
// Case 1 : Left left - Trái trái if (ll) { root = rotateRight(root);// Quay phải tại nút gốc root->color = 0; // Chuyển nút gốc thành Black root->right->color = 1; // Đổi màu con phải nút gốc thành Red ll = false;
// Case 2 : Right right - Phải phải else if (rr) { root = rotateLeft(root); // Quay phải tại nút gốc root->color = 0; // Đổi màu nút gốc thành Black root->left->color = 1; // Đổi màu bên trái nút gốc thành Red rr = false;
// Case 3 : Left right - Phải trái else if (lr) { root->left = rotateLeft(root->left);// Quay trái tại nút bên trái gốc root->left->parent = root; root = rotateRight(root); // Quay phải nút gốc root->color = 0; // Đổi màu nút gốc thành Black root->right->color = 1; // Đổi màu bên phải nút gốc thành đỏ lr = false;
// Case 4 : Right left - Phải trái else if (rl) { root->right = rotateRight(root->right); root->right->parent = root; root = rotateLeft(root); root->color = 0; root->left->color = 1; rl = false;
// Xử lí xung đột đỏ - RED RED if (f) { if (root->parent->right == root) { if (root->parent->left == NULL || root->parent->left->color = 0) {
// Cha đỏ - chú đen (rr or rl) if (root->left != NULL && root->left->color == 1) rl = true; if (root->right != NULL && root->right->color == 1) rr = true;
// Cha đỏ - chú đỏ root->color = root->parent->left->color = 0; if (root != Root) root->parent->color = 1;
} else { if (root->parent->right == NULL || root->parent->right->color
// Cha đỏ - chú đen (ll or lr) if (root->left != NULL && root->left->color == 1) ll = true; if (root->right != NULL && root->right->color == 1) lr = true;
// Cha đỏ - chú đỏ root->color = root->parent->right->color = 0; if (root != Root) root->parent->color = 1;
} void Insert(int key) { if (Root == NULL) {
Root = new Node{ key, NULL, NULL, NULL, 0 };
Root = insertHelp(Root, key); if (Root->color == 1)
}; string getColor(Node* root) { if (root->color == 1) return "RED"; return "BLACK";
// In - Print 2D ra console void print2DUtil(Node* root, int space) { if (root == NULL) return; space += COUNT; print2DUtil(root->right, space); cout left == current) return current->parent->right; return current->parent->left;
} void swapColors(Node* x1, Node* x2) { bool temp; temp = x1->color; x1->color = x2->color; x2->color = temp;
} void swapValues(Node* u, Node* v) { int temp; temp = u->data; u->data = v->data; v->data = temp;
} bool hasRedChild(Node* x) { if (x->right != NULL) if (x->right->color == 1) return true; if (x->left != NULL) if (x->left->color == 1) return true; return false;
} string getColor(Node* root) { if (root->color == 1) return "RED"; return "BLACK";
// In - Print 2D ra console void print2DUtil(Node* root, int space) { if (root == NULL) return; space += COUNT; print2DUtil(root->right, space); cout right->parent = root; x->parent = root->parent; if (root->parent == NULL)
Root = x; else if (root->parent->left == root) root->parent->left = x; else root->parent->right = x;
// Gán root vào x.right x->right = root; root->parent = x;
// Gán y->left vào right root root->right = y->left; if (y->left != NULL) y->left->parent = root; y->parent = root->parent; if (root->parent == NULL)
Root = y; else if (root->parent->left == root) root->parent->left = y; else root->parent->right = y;
// Gán root vào y.left y->left = root; root->parent = y;
Node* insertHelp(Node* root, int key) {
// f đúng khi có xung đột RED RED bool f = false; if (root == NULL) { return new Node{ key, NULL, NULL, NULL, 1 }; // RED Node
} else if (key < root->data) { root->left = insertHelp(root->left, key); root->left->parent = root;
// root->left = Node X // root = X->parent if (Root != root) { if (root->color == root->left->color == 1) f = true;
} } else { root->right = insertHelp(root->right, key); root->right->parent = root;
// root->right = Node X // root = X->parent if (Root != root) { if (root->color == root->right->color == 1) f = true;
// Xử lý 4 TH lệch // *** Khi này (ll, lr, rr, rl = false) nên chưa xử lí liền
// *** Sau khi thoát 1 vòng đệ quy thì: root = X->parent-
// *** Tức là Node ông, lúc này ta quay Node ông
// Case 1 : Left left - Trái trái if (ll) { root = rotateRight(root); root->color = 0; root->right->color = 1; ll = false;
} // Case 2 : Right right - Phải phải else if (rr) { root = rotateLeft(root); root->color = 0; root->left->color = 1; rr = false;
} // Case 3 : Left right - Phải trái else if (lr) { root->left = rotateLeft(root->left); root->left->parent = root; root = rotateRight(root); root->color = 0; root->right->color = 1; lr = false;
} // Case 4 : Right left - Phải trái else if (rl) { root->right = rotateRight(root->right); root->right->parent = root; root = rotateLeft(root); root->color = 0; root->left->color = 1; rl = false;
// Xử lí xung đột đỏ - RED RED if (f) { if (root->parent->right == root) { if (root->parent->left == NULL || root-
// Cha đỏ - chú đen (rr or rl) if (root->left != NULL && root->left-
>color == 1) rl = true; if (root->right != NULL && root->right-
// Cha đỏ - chú đỏ root->color = root->parent->left->color 0; if (root != Root) root->parent->color = 1;
} } else { if (root->parent->right == NULL || root-
// Cha đỏ - chú đen (ll or lr) if (root->left != NULL && root->left-
>color == 1) ll = true; if (root->right != NULL && root->right-
// Cha đỏ - chú đỏ root->color = root->parent->right->color
= 0; if (root != Root) root->parent->color = 1;
} void Insert(int key) { if (Root == NULL) {
Root = new Node{ key, NULL, NULL, NULL, 0 };
Root = insertHelp(Root, key); if (Root->color == 1)
} } void fixDoubleBlack(Node* root) { if (root == Root) return;
Node* par = root->parent; if (sib == NULL) fixDoubleBlack(par); else { if (sib->color == 1) { par->color = 1; sib->color = 0; if (sib->parent->left == sib) par = rotateRight(par); else par = rotateLeft(par); fixDoubleBlack(root);
} else { if (hasRedChild(sib)) { if (sib->parent->left == sib) { if (sib->left != NULL && sib->left-
>color) { sib->left->color = sib->color; sib->color = par->color; par->color = 0; par = rotateRight(par);
} else { sib->right->color = par-
>color; par->color = 0; sib = rotateLeft(sib); par = rotateRight(par);
} } else { if (sib->right != NULL && sib-
>right->color) { sib->right->color = sib-
>color; sib->color = par->color; par->color = 0; par = rotateLeft(par);
} else { sib->left->color = par->color; par->color = 0; sib = rotateRight(sib); par = rotateLeft(par);
} else { sib->color = 1; if (par->color == 0) fixDoubleBlack(par); else par->color = 0;
Node* uReplace; if ((vDelete->left != NULL) && (vDelete->right != NULL)) uReplace = maxValueNode(vDelete->left); else if (vDelete->left != NULL) uReplace = vDelete->left; else if (vDelete->right != NULL) uReplace = vDelete->right; else uReplace = NULL; bool uvBlack = ((uReplace == NULL) || (uReplace->color = 0)) && (vDelete->color == 0);
Node* sib = sibling(vDelete); if (uReplace == NULL) { if (vDelete == Root) {
} else { if (uvBlack) fixDoubleBlack(vDelete); else if(sib != NULL) sib->color = 1; if (vDelete->parent->left == vDelete) par->left = NULL; else par->right = NULL;
} if (vDelete->left == NULL || vDelete->right == NULL) { if (vDelete == Root) { vDelete->data = uReplace->data; vDelete->left = vDelete->right = NULL; delete uReplace;
} else { if (vDelete->parent->left == vDelete) par->left = uReplace; else par->right = uReplace; delete vDelete; uReplace->parent = par; if (uvBlack) fixDoubleBlack(uReplace); else uReplace->color = 0;
} swapValues(uReplace, vDelete); deleteNode(uReplace);
Node* temp = Root; while (temp != NULL) { if (val < temp->data) { if (temp->left == NULL) return NULL; else temp = temp->left;
} else if (val == temp->data) { break;
} else { if (temp->right == NULL) return NULL; else temp = temp->right;
Node* vDelete = search(val); if (vDelete == NULL) { cout