Cây đỏ đen là 1 loại cây thông dụng trong nhiều lĩnh vực,đây là bài tập lớn môn cấu trúc dữ liệu của mình,nếu bạn nào muốn lấy code thì liên hệ vs mình mình gửi,
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI Viện Công nghệ Thông tin và Truyền thông
BÀI TẬP LỚN MÔN HỌC CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT
Đề tài:
" Red -Black Tree"
Sinh viên thực hiện:
Phùng Văn Chiến SHSV:20111253
Mã lớp:
Giáo viên hướng dẫn: PGS Nguyễn Đức Nghĩa
HÀ NỘI – 2013
Trang 2
Mục lục
I.Định nghĩa 3
A.Giới Thiệu: 3
B.Cây đỏ đen 3
1.Đinh nghĩa: 3
2.Các tính chất của cây đỏ đen: 3
3.Chiều cao đen 4
4.Các định lí: 4
5.Khai báo cấu trúc 5
6.Các phép toán trên cây đỏ-đen 5
II.Cài đặt 5
1.Khởi tạo 5
2.Tạo một nút mới 6
3.Các phép quay 6
4.Phép chèn(insertation) 7
5.Phép xóa(Deletation) 11
6.Tìm Kiếm(Search): 15
III.Ứng Dụng 16
Trang 3I.Định nghĩa
A.Giới Thiệu:
Cây tìm kiếm nhị phân là một cấu trúc lưu trữ dữ liệu tốt với tốc độ tìm kiếm nhanh Tuy nhiên trong một số trường hợp cây tìm kiếm nhị phân có một số hạn chế Nó hoạt động tốt nếu dữ liệu được chèn vào cây theo thứ tự ngẫu nhiên Tuy nhiên, nếu dữ liệu được chèn vào theo thứ tự đã đuợc sắp xếp sẽ không hiệu quả Khi các trị số cần chèn đã đuợc sắp xếp thì cây nhị phân trở nên không cân bằng Khi cây không cân bằng, nó mất đi khả năng tìm kiếm nhanh (hoặc chèn hoặc xóa) một phần tử đã cho Chúng ta khảo sát một cách giải quyết vấn đề của cây không cân bằng: đó là cây đỏ đen, là cây tìm
B.Cây đỏ đen
1.Đinh nghĩa:
Cây đỏ-đen là một dạng cây tìm kiếm nhị phân tự cân bằng ,một cấu trúc dữ liệu được sử dụng trong khoa học máy tính.Cấu trúc ban đầu của nó được đưa ra vào năm 1972 bởi Rudolf Bayer Ông gọi chúng là “B-cây cân bằng”, còn tên hiện nay được đưa ra năm 1978 bởi Leo J Guibas và Robert Sedgewick.Nó là cấu trúc phức tạp nhưng cho kết quả tốt về thời gian trong trường hợp xấu nhất Các phép toán trên chúng như tìm kiếm (search), chèn (insert), và xóa (delete) trong thời gian
O(logn), trong đó n là số các phần tử của cây
2.Các tính chất của cây đỏ đen:
Tc1:Một nút hoặc là đỏ hoặc là đen.
Tc2:Gốc là đen.
Tc3:Tất cả các lá là đen.
Tc4:Cả hai con của mọi nút đỏ là đen (và suy ra mọi nút đỏ có nút cha là đen.)
Tc5:Tất cả các đường đi từ một nút đã cho tới các lá chứa một số như nhau các nút đen.
Tính chất 5 còn được gọi là tính chất "cân bằng đen" Số các nút đen trên một đường đi từ gôc tới mỗi lá được gọi là độ dài đen của đường đi đó Sức mạnh của cây đỏ đen nằm trong các tính chất trên
Từ các tính chất này suy ra trong các đường đi từ gốc tới các lá đường đi dài nhất không vượt quá hai lần đường đi ngắn nhất Do đó cây đỏ đen là gần cân bằng Vì các thuật toán chèn, xóa, tìm kiếm trong trường hợp xấu nhất đều tỷ lệ với chiều cao của cây nên cây đỏ đen rất hiệu quả trong các trường hợp xấu nhất,không giống như cây tìm kiếm nhị phân thông thường
Tuy nhiên có thể biểu diễn cây đỏ đen ta có một chút thay đổi mà không làm thay đổi tính chất cơ bản
của cây và Thời Gian Tính của các thuật toán Với mục đích này, ta đưa thêm các lá null vào làm con
phải hoặc con trái hoặc cả hai của những nút không có chúng, các lá này không chứa dữ liệu mà chỉ làm nhiệm vụ thông báo rằng tại đây cây đã kết thúc, như hình vẽ ở dưới Việc thêm các nút này làm cho tất cả các nút trong của cây đều chứa dữ liệu và có hai con, hay khác đi cây đỏ đen cùng với các lá
Trang 4Hình 1: Ví dụ về cây đỏ-đen
3.Chiều cao đen
Chiều cao đen (Black -Height) của một nút P bằng số nút đen trên đường dẫn từ nút đó đến nút lá bất
kì (không tính các nút von trỏ Null).Kí hiệu là bh(P).Ví dụ như hình vẽ trên có bh(root)=2
4.Các định lí:
Định lí 1:Một cây đỏ-đen bất kì có nút gốc là x có ít nhất n=2bh(x)- 1 nút trong,bh(x) là chiều cao đen của gốc x
Chứng minh:Ta chứng minh bằng quy nạp với 1 nút x bất kì(x không nhất thiết là nút gốc màu đen,ở đây ta tổng quát cho cả trường hợp x là nút đỏ hay x là con của 1 nút gốc khác)
+) Nếu bh=0 thì x=NULL,do đó số nút trong =0=2
bh(x)
- 1
+) Nếu bh=1 thì x<> NULL,số nút trong ít nhất là 1=2
bh(x)
- 1
+)Giả sử các cây có chiều cao đen =bh(x)-1 đều thỏa mãn định lí.Xét cây gốc x có chiều cao đen bh(x),khi đó 2 con của nó có chiều cao đen hoặc =bh(x)-1(nếu x đen) hoặc =bh(x) (nếu x đỏ).Do đó số nút tối thiểu của cây gốc x bằng số nút trên 2 cây con cộng 1
(2
bh(x)-1
- 1)+(2
bh(x)-1
- 1)=2
bh(x)
- 1
==> ĐPCM
Định lí 2:Trong một cây đỏ đen,ít nhất một nửa số nút trên đường dẫn từ gốc đến lá là nút đen.
=> Chứng minh dựa vào tính chất 4(mọi nút đỏ đều có 2 con là nút đen do đó không có 2 nút đỏ kề nhau trên đường đi từ gốc đến là)
Định lí 3:Một cây đỏ-đen có n nút trong có chiều cao h<=2log(n+1).
Chứng minh :
Gọi h là chiều cao cây đỏ-đen gốc x
Trang 5Theo định lí 2 ta có bh(x)>=h/2.
Mặt khác từ định lí 1 ta có n>=2bh(x)- 1>=2h/2-1
Do đó h<=2log(n+1)
==> ĐPCM
5.Khai báo cấu trúc
typedef int Data; /* Kiểu dữ liệu khoá */
typedef enum { BLACK, RED } nodeColor;
typedef struct Node
{
nodeColor color; /* Màu node (BLACK, RED) */
Data info; /* Khoá sử dụng tìm kiếm */
struct Node *left; /* Con trái */
struct Node *right; /* Con phải */
struct Node *parent; /* Cha */
};
typedef struct Node pNode
typedef struct RedBlackTree{pNode **root};
struct RedBlackTree Tree;
6.Các phép toán trên cây đỏ-đen
-init(item):Khởi tạo cây có gốc có khóa là item.
- makeRedBlack(value,node):tạo 1 nút với khóa cho bởi value,và có nút cha là nút node
- rotate_left(RedBlack),rotate_right(RedBlack): quay trái quay phải nút RedBlack
- insert(item):chèn nút với khóa cho bởi item vào cây đỏ-đen ;
- delete(item):xóa nút cho bởi khóa item trong cây đỏ-đen;
- search(item):tìm nút với khóa item trong cây đỏ-đen;
II.Cài đặt
1.Khởi tạo.
pNode * init(Data item)
{
Tree.root=(pNode *)malloc(sizeof(pNode *));
Tree.root->info=item;
Tree.root->color=BLACK;
Tree.root->left=NULL;
Tree->right=NULL;
Tree->parent=NULL;
return Tree.root;
}
Trang 62.Tạo một nút mới.
Input:phần tử cần tạo;
Các bước:
+) Cấp phát bộ nhớ cho nút mới
+)Kiểm tra lỗi cấp phát;
+) Nếu cấp phát được thì đưa phần tử vào nút mới và gán nó màu đỏ (mặc định);
+)Con trái con phải gán bằng NULL
OutPut:Con trỏ tới địa chỉ nút mới
pNode *makeRedBlack(Data x,pNode *node)
{
pNode * newRedBlack=NULL;
newRedBlack=(pNode *)malloc( sizeof( pNode *)); //khởi tạo
if(newRedBlack==NULL)
{
printf(”Out of memmory”) exit(1);
}
else
{
newRedBlack->info=x;
newRedBlack->color=RED;
newRedBlack->left=NULL;
newRedBlack->right=NULL;
newRedBlack->parent=node;
}
return newRedBlack;
}
Thời Gian Tính: Dễ thấy Thời Gian Tính của thao tác là O(1).
3.Các 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ó
Trang 7hình 2:quay trái,quay phải
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
a.Quay trái
void rotate_left(pNode * x)
{
pNode * y=x->right;// set node
x->right=y->left;//con phải của x được gán bằng con trái của y
if(y->left!=NULL) y->left->parent=x;//cha của nút con trái của y giờ là x
y->parent=x->parent;//gán cha cho y là cha của x
if(x->parent==NULL) //x là nút gốc
{
T.root =y; //thì y giờ thành nút gốc.
}
else if(x==x->parent->left) //Trái lại thì gán cha của x cho cha y.
{
x->parent->left=y;
}
else x->parent->right=y;
y->left=x; //đặt lại con trái cho node và cha của RedBlack;
x->parent=y;
}
b.Quay phải
void rotate_right(pNode * x)
{
pNode * y=x->left;// set y
x->left=y->right;//con trái của x được gán bằng con phải của y
if(y->right!=NULL) y->right->parent=x;//cha của nút con phải của y giờ là x
y->parent=x->parent;//gán cha cho y là cha của x
if(x->parent==NULL)
{
T.root=y;//x là nút gốc thì y giờ thành nút gốc.
}
else if(x==x->parent->left) //Trái lại thì gán cha của x cho cha y.
{
x->parent->left=y;
}
else x->parent->right=y;
y->right=x; //đặt lại con phải cho y và cha của x;
x->parent=y;
}
.Thời Gian Tính:Các thao tác quay đều có Thời Gian Tính là hằng số :O(1).
4.Phép chèn(insertation)
-Phép chèn bắt đầu bằng việc bổ sung một nút như trong cây tìm kiếm nhị phân bình thường và gán
cho nó màu đỏ.Ta xem xét để bảo toàn tính chất đỏ đen từ các nút lân cận với nút mới bổ xung.Thuật
ngữ nút chú bác sẽ dùng để chỉ nút anh(hoặc em) với nút cha của nút đó như trong cây phả hệ.Chú ý:
Trang 8Tính chất 3(Tất cả các lá đều đen-là các nút NULL) giữ nguyên.
Tính chất 4(Cả 2 con của nút đỏ là đen) nếu bị thay vì thêm một nút đỏ có thể gán màu đen cho
1 nút đỏ hoặc thực hiện phép quay
Tính chất 5(Tất cả các đường đi từ gốc tới các lá có cùng một số nút đen) nếu bị thay đổi vì việc thêm 1 nút đỏ có thể gán 1 nút đỏ thành đen hoặc thực hiện phép quay
-Khi chèn cần chú ý các trường hợp sau(giả sử X trỏ đến nút vừa thêm):
CASE 1:X là nút gốc,đổi màu X thành đen;(được thực hiện trong hàm makeRedBlack() )
CASE 2: Cả 2 nút: cha và chú đều đỏ thì thực hiện đổi mầu nút cha, chú thành đen, màu nút ông thành
đỏ, X trỏ đến nút ông, tiếp tục kiểm tra X tại vị trí mới
hình 3:minh họa thao tác
nút ông thành đỏ, X –đen, quay trái(phải) nút cha, quay phải(trái) nút ông
Hình 4:minh họa thao tác trên
Trang 9(nút cha quay trái,nút ông quay phải)
CASE 4: Nút cha đỏ, chú đen X và nút cha hoặc cùng là con trái hoặc cùng là con phải Thực hiện đổi màu nút cha thành đen, ông thành đỏ, quay phải nút ông
hình 5:minh họa thao tác trên.
a.Hàm sau minh họa thao tác chèn khóa có giá trị item vào cây đỏ-đen được trỏ
bởi con trỏ RedBlack;
void insert(Data item)
{
pNode * x=Tree.root;
pNode * y=x->parent;
while(x!=NULL)
{
if(item<x->info)
{
y=x;
x=x->left;
}
else if(item>x->info)
{
y=x;
x=x->right;
}
x=makeRedBlack(item,y);
changecolor(x);//thao tác đổi màu theo nguyên tắc trên
}
Trong chương trình chính để chèn khóa key ta gọi insert(T.root,key)
b.Hàm xác định nút chú bác và nút ông:
pNode * grandparent(pNode * RedBlack)
Trang 10{
return RedBlack->parent->parent;
}
pNode * uncle(pNode * RedBlack)
{
pNode * tmp;
tmp=grandparent(RedBlack);
if(RedBlack->parent==tmp->left) return tmp->right;
else return tmp->left;
}
c Hàm đổi màu:changecolor(RedBlack) {đổi màu theo các nguyên tắc đã nêu ở trên}
void changecolor(pNode * RedBlack)
{
pNode * grandpt;
pNode * ule;
grandpt=grandparent(RedBlack);
ule=uncle(RedBlack);
if(RedBlack->parent==NULL) //case 1
{
RedBlack->color=BLACK;
}
else if(RedBlack->parent->color==RED&&ule->color==RED) //case 2
{
RedBlack->parent->color=BLACK;
ule->color=BLACK;
grandpt->color=RED;
changecolor(T,grandpt);
}
else if(RedBlack->parent->color==RED&&(ule->color==BLACK|| ule==NULL))
{
if(RedBlack->parent==grandpt->left&&RedBlack==RedBlack->parent->right) //case 3 {
grandpt->color=RED;
RedBlack->color=BLACK;
rotate_left(T,RedBlack->parent);//quay trái;
rotate_right(T,grandpt);//quay phải;
}
else if(RedBlack->parent==grandpt->right&&RedBlack==RedBlack->parent->left) //case 3 {
grandpt->color=RED;
RedBlack->color=BLACK;
rotate_right(T,RedBlack->parent);//quay trái;
rotate_left(T,grandpt);//quay phải;
}
else if(RedBlack->parent==grandpt->left&&RedBlack==RedBlack->parent->left) //case 4 {
RedBlack->parent->color=BLACK;
grandpt->color=RED;
rotate_right(T,grandpt);
Trang 11}
else if(RedBlack->parent==grandpt->right&&RedBlack==RedBlack->parent->right) //case 4 {
RedBlack->parent->color=BLACK;
grandpt->color=RED;
rotate_left(T,grandpt);
}
}
}
Thời Gian Tính:
- Do cây đỏ đen là cây cân bằng nên nếu duyệt theo chiều cao của cây thì luôn có thời gian là O(logn)
- Hàm changecolor(RedBlack) có thời gian là O(logn) trong trường hợp 1(đi từ dưới lên trên gốc) và O(1) trong các trường hợp còn lại
- Hàm insert():
Việc duyệt nút từ gốc xuống tìm vị trí chèn có thời gian là O(logn)
Hàm đổi màu tối đa là O(logn)(case 1)
Hàm tạo nút là O(1)
=> Tổng là O(logn).
5.Phép xóa(Deletation)
Trong cây tìm kiếm nhị phân bình thường khi xóa một nút có cả hai con (không là lá null), ta tìm phần
tử lớn nhất trong cây con trái hoặc phần tử nhỏ nhất trong cây con phải, chuyển giá trị của nó vào nút
có ít hơn hai con (không là lá null) Vì việc copy giá trị không làm mất tính chất đỏ đen nên không cần phải sửa chữa gì cho thao tác này Việc này chỉ đặt ra khi xóa các nút có nhiều nhất một con (không là
lá null)
Chúng ta sẽ thảo luận về việc xóa một nút có nhiều nhất một con (không là lá null)
Nếu ta xóa một nút đỏ, ta có thể chắc chắn rằng con của nó là nút đen Tất cả các đường đi đi qua nút
bị xóa chỉ đơn giản bớt đi một nút đỏ do đó tính chất 5 không thay đổi Ngoài ra, cả nút cha và nút con của nút bị xóa đều là nút đen, do đó tính chất 3 và 4 vẫn giữa nguyên Một trường hợp đơn giản khác
là khi xóa một nút đen chỉ có có một con là nút đỏ Khi xóa nút đó các tính chất 4 và 5 bị phá vỡ, nhưng nếu gán lại màu cho nút con là đen thì chúng lại được khôi phục
Trường hợp phức tạp xảy ra khi cả nút bị xóa và nút con của nó đều là đen Chúng ta sẽ bắt đầu bằng
việc thay nút bị xóa bằng nút con của nó Chúng ta sẽ gọi nút con này (trong vị trí mới của nó là N, và anh em với nó (con khác của nút cha mới) là S Tiếp theo ta vẫn dùng P chỉ cha mới của N, SL chỉ con trái của S, và SR chỉ con phải của S (chúng tồn tại vì S không thể là lá).
Hàm xóa nút có khóa là k:
void deleteRedBlack(Data k)
{
pNode *x,*y,*z;
pNode *Null=null();
if(Tree.root==NULL)
{
printf("\ncay rong khong the xoa");
return;
}