Professional Documents
Culture Documents
TP.HCM – 12/2008
Đại Học Sư Phạm Tp. Hồ Chí Minh
Thông tin giảng viên
• LƯƠNG TRẦN HY HIẾN
• Bộ Môn Tin Học
CẤU TRÚC DỮ LIỆU 2
• Khoa Toán – Tin học
• Phone: 0949 790 775
• Email: hienlth@hcmup.edu.vn
• Tài liệu:
http://sites.google.com/a/hcmup.edu.vn/hienlth
Giới thiệu về môn học
3 4
Đề cương môn học: CTDL2 Đánh giá kết quả học tập
• Chương 1: Sắp xếp ngoại • Thi lý thuyết : 70%
• Chương 2: Bảng băm (Hash Table) • Thi thực hành: 30%
• Chương 3: B – Cây (B-Tree)
• Bài thực hành hàng tuần: Kết quả
• Chương 4: Cây Đỏ Đen (Red-Black Tree) không tính vào điểm tổng kết, tuy
nhiên nếu không thực hiện tốt sẽ bị trừ
10% nội dung thi thực hành.
5 6
Thuật toán trộn 1 – Trộn từng phần tử T.toán trộn 2 – Trộn dãy phần tử được sắp
int nCurrA = 1;
int nCurrA = 1; int nCurrB = 1;
int nCurrB = 1; while (nCurrA <= N && nCurrB <= M)
A[N+1] = MAX_VALUE; // phần tử lính canh {
if (A[nCurrA] < B[nCurrB])
B[M+1] = MAX_VALUE; // phần tử lính canh C[nCurrC] = A[nCurrA];
for (int nCurrC=1; nCurrC <= N+M; nCurrC++) else
if (A[nCurrA] < B[nCurrB]) { C[nCurrC] = B[nCurrB];
nCurrC++;
C[nCurrC] = A[nCurrA]; }
nCurrA++; // Xét phần tử còn lại của dãy A
} for ( ; nCurrA <= N; nCurrA++) {
C[nCurrC] = A[nCurrA];
else { nCurrC++;
C[nCurrC] = B[nCurrB]; }
nCurrB++; // Xét phần tử còn lại của dãy B
for ( ; nCurrB <= M; nCurrB++) {
} C[nCurrC] = B[nCurrB];
nCurrC++;
}
CTDL2 – Lương Trần Hy Hiến 7 CTDL2 – Lương Trần Hy Hiến 8
1. Phương pháp trộn trực tiếp 1. Phương pháp trộn trực tiếp
• Khái niệm: • Giải thuật:
– Run là một dãy liên tiếp các phần tử được sắp – Giải thuật sắp xếp tập tin bằng phương pháp
thứ tự. trộn Run có thể tóm lược như sau:
Ví dụ: 2 4 7 12 55 là một Run. Input: f0 là tập tin cần sắp thứ tự
– Chiều dài Run chính là số phần tử trong Run. Ouput: f0 là tập tin đã được sắp thứ tự
Chẳng hạn Run ở ví dụ trên có chiều dài là 5. Gọi f1, f2 là 2 tập tin trộn.
Trong đó f0, f1, f2 có thể là các tập tin văn bản
thường (text file) hay các tập tin nhị phân.
1. Phương pháp trộn trực tiếp 1. Phương pháp trộn trực tiếp
• Bước 1: • Bước 2:
– Giả sử các phần tử trên f0 là: – Phân bố m=2 *m = 2 phần tử từ f0 vào f1, f2
24 12 67 33 58 42 11 34 29 31 f0: 12 24 33 67 42 58 11 34 29 31
– Khởi tạo f1, f2 rỗng. f1: 12 24 42 58 29 31
– Thực hiện phân bố m=1 phần tử từ f0 lần lượt f2: 33 67 11 34
vào f1, f2: – Trộn f1, f2 thành f0:
f1: 24 67 58 11 29 f0: 12 24 33 67 11 34 42 58 29 31
f2: 12 33 42 34 31
– Trộn f1,f2 vào f0
f0: 12 24 33 67 42 58 11 34 29 31
3. Phương pháp trộn đa lối cân bằng 3. Phương pháp trộn đa lối cân bằng
Ví dụ:
Thuật toán:
• fInput: U Q N M K I H F D C B, N=3
• B1: Gọi tập nguồn S = {f1, f2, …, fn} // Phân phối (lần 1)
Gọi tập đích D = {g1, g2, …, gn} f1: U M H C
Chia xoay vòng dữ liệu của file F0 cho các file thuộc f2: Q K F B
tập nguồn, mỗi lần 1 Run cho tới khi F0 hết. f3: N I D
// Trộn (lần 1)
• B2: Trộn từng bộ Run của các file thuộc tập nguồn g1: N Q U B C
S, tạo thành Run mới, mỗi lần ghi lên các file thuộc g2: I K M
tập đích D. g3: D F H
• B3: Nếu (số Run trên các file của D > 1) thì: // Trộn (lần 2)
f1: D F H I K M N Q U
– Hoán vị vai trò tập nguồn (S) và tập đích (D). f2: B C
– Quay lại B2 f3: NULL
Ngược lại kết thúc thuật toán. // Trộn (lần 3)
g1: B C D F H I K M N Q U
g2: NULL
CTDL2 – Lương Trần Hy Hiến 23 g3: NULL CTDL2 – Lương Trần Hy Hiến 24
3. Phương pháp trộn đa lối cân bằng 3. Phương pháp trộn đa lối cân bằng
• Các ký hiệu:
– fInput: file dữ liệu gốc cần sắp xếp Thuật toán chi tiết:
– N: số phần tử trên file fInput [Bước 1] S = {f1, f2, … , fn}
– n: số file trung gian trên mỗi tập nguồn/đích D = {g1, g2, … , gn}
– S: tập các file nguồn // Chia xoay vòng dữ liệu của fInput cho các
– D: tập các file đích file thuộc tập nguồn S
– Sdd: tập các file nguồn đang còn run dở dang i = 1;
– Str: tập các file nguồn chưa hết (!EOF), còn có thể tham gia vào quá
trình trộn while (!feof(fInput)) {
– “Lượt”: là 1 quá trình trộn run từ nguồn đ đích, một “luợt” kết thúc khi Copy_1_Run(fInput, fi);
mỗi file đích (trong tập D) nhận được 1 run i = (i % n) + 1;
– Drun: tập các file đích đã nhận được run trong “lượt” hiện hành
}
• Suy diễn:
– S –Str: tập các file nguồn đã hết (EOF) Str = S;
– Str –Sdd: tập các file nguồn chưa hết (!EOF), nhưng đã kết thúc run Drun = {};
hiện tại nDemRun = 0;
– D – Drun: tập các file đích chưa nhận được run trong “lượt” hiện hành
CTDL2 – Lương Trần Hy Hiến 25 CTDL2 – Lương Trần Hy Hiến 26
3. Phương pháp trộn đa lối cân bằng 3. Phương pháp trộn đa lối cân bằng
[Bước 2]
ngược lại { // File f0 chưa hết
a. Sdd = Str
b. Gọi dhh ∈ D –Drun là file đích hiện hành (sẽ được nhận run) Nếu (!EOR(f0)) thì {
c. Đọc các phần tử xfi, fi ∈ Sdd Đọc phần tử kế xf0 từ file f0;
d. Gọi xf0 = MIN { xfi, fi ∈ Sdd} Goto [Bước 2.d]
e. Copy xf0 lên dhh }
f. Nếu (file f0 hết) thì {
Str = Str – {f0}
ngược lại { // Hết run hiện hành trên f0
Sdd = Sdd – {f0} Sdd = Sdd – {f0}
Nếu (Str == {}) thì { // Xong quá trình trộn N đ D Nếu (Sdd <> {}) thì Goto [Bước 2.d]
nDemRun++; ngược lại { // Sdd=={}: hết bộ run hiện hành
Goto [Bước 3]
nDemRun++;
}
ngược lại Nếu (Sdd <> {}) thì Goto [Bước 2.d] Drun = Drun + {dhh};
ngược lại { // Sdd=={}: hết bộ run hiện hành Nếu (Drun==D) thì Drun= {}; // Xong 1 “lượt”
nDemRun++; Goto [Bước 2.a]
Drun = Drun + {dhh}; }
Nếu (Drun==D) thì Drun= {}; // Xong 1 “lượt”
Goto [Bước 2.a]
} // end of file f0 chưa hết
} CTDL2 – Lương Trần Hy Hiến 27 CTDL2 – Lương Trần Hy Hiến 28
3. Phương pháp trộn đa lối cân bằng Trộn đa lối cân bằng
[Bước 3] • Ví dụ: Cho dãy số sau
Nếu (nDemRun == 1) thì Kết thúc thuật toán 3 5 2 7 12 8 4 15 20 1 2 8 23 7 21 27
ngược lại { • Nhập :
Nếu (nDemRun < n) thì Str = Drun; // Không đủ n run
f0 :3 5 2 7 12 8 4 15 20 1 2 8 23 7 21 27
ngược lại Str = D;
Drun = {}; • Xuất :
nDemRun = 0; f0: 1 2 2 3 4 5 7 7 8 8 12 15 20 21 23 27
“Hoán vị tập S, D”
Goto [Bước 2]
}
Các bước tiến hành : chọn 6 file Các bước tiến hành : chọn 6 file
Bước 2:
3 5 2 7 12 8 4 15 20 1 2 8 23 7 21 27 -Trộn các run của f[1], f[2], f[3] và luân phiên phân phối vào các
Bước 0: đặt nh = 3 file g[1], g[2], g[3]
g1: 2 3 5 7 8 12
Bước 1:
g2: 1 2 4 7 8 15 20 21 23 27
Phân phối các run luân phiên vào f[1], f[2], f[3] g3:
f1: 3 5 4 15 20 - Do số run sau khi trộn >1 nên tiếp tục trộn run từ g[1], g[2], g[3]
vào ngược trở lại f[1], f[2], f[3]
f2: 2 7 12 1 2 8 23 f1: 1 2 2 3 4 5 7 7 8 8 12 15 20 21 23 27
f3: 8 7 21 27 f2:
f3:
- Do số run trộn = 1 nên kết thúc thuật toán
CTDL2 – Lương Trần Hy Hiến 31 CTDL2 – Lương Trần Hy Hiến 32
Bước 2
Bước 1
3 5 2 7 12 8 4 15 20 1 2 8 23 7 21 27 f1 3 5 4 15 20
f2 2 7 12 1 2 8 23
f3 8 7 21 27
f1
f2 3 2 8 7 5 12
f3
g1
g2 1 2 4 7 8 15 20 21 23 27
g3
• Output file: File sẽ chứa trị của biến • Ghi dữ liệu chuẩn
– Tương tự như cout
khi trị của biến được ghi vào
– <tên biến> <toán tử xuất (<<)>
– Ví dụ: int a;
fout<<a;
2. ofstream 2. ofstream
• Ghi dữ liệu dạng nhị phân • Ví dụ
– ostream& write(char* pch,int nCount); Ghi dữ liệu của 5 phần tử số nguyên liên tiếp ra
– ostream& write(unsigned char* puch,int nCount); file dạng nhị phân
– ostream& write(signed char* psch,int nCount); ---------------------------------------------------------
– Giải thích tham số
• pch,puch,psch: con trỏ đến mảng ký tự lưu dữ liệu.
int a[10];
• nCount: số byte cần ghi a[0]=65;a[1]=66;a[2]=97;a[3]=99;a[4]=67;
• Thường dùng để ghi toàn bộ một struct hoặc ofstream fout(“data.dat”);
một đối tượng ra file
int n=5;
• Đóng tập tin: sử dụng phương thức close();
fout.write((char*)a,n*sizeof(*a));
fout.close();
CTDL2 – Lương Trần Hy Hiến 47 CTDL2 – Lương Trần Hy Hiến 48
3. ifstream 3. ifstream
• Khai báo và mở tập tin để đọc dữ liệu
– ifstream <tên biến>;
• Đọc dữ liệu dạng nhị phân
– istream& read(char* pch, int nCount);
– ifstream <tên biến>(<tên file>); – istream& read(unsigned char* puch, int nCount);
– Ví dụ: ifstream fin; – istream& read(signed char* psch, int nCount);
fin.open(“data.dat”); • Giải thích tham số
hoặc ifstream fin(“data.dat”); – pch,puch,psch: con trỏ đến mảng ký tự lưu dữ
• Đọc dữ liệu chuẩn liệu.
– nCount: số byte lớn nhất cần đọc
– Tương tự như cin
• Thường dùng để đọc một struct hoặc một
– <tên biến> <toán tử nhập (>>)> đối tượng từ file
– Ví dụ: int a; • Đóng tập tin: sử dụng phương thức close();
fin>>a;
5. fstream
• Sử dụng đối tượng của lớp này có thể vừa
đọc vừa ghi dữ liệu trong cùng một tập tin
• is_open(): Trả về true nếu đang mở, false
nếu không mở.
• Mở tập tin vừa đọc vừa ghi dạng nhị phân Làm việc với file bằng C
fstream f;
f.open(“data.dat”,ios::in|ios::out|ios::binary);
ios::in, ios::out, ios::app, ios::nocreate, …
– Ví dụ: “rb” Mở một tập tin đã có để đọc theo kiểu nhị phân
FILE *f; “wb” Mở một tập tin mới để ghi theo kiểu nhị phân
f = fopen(“D:\\data.txt”,”r”); //mở tập tin để đọc dữ liệu. “ab” Mở một tập tin mới để ghi bổ sung theo kiểu nhị phân
f = fopen(“D:\\data.txt”,”w”); //mở tập tin để ghi dữ liệu. Lưu ý: Cần làm sạch vùng đệm trước khi chuyển từ đọc
sang ghi hoặc từ ghi sang đọc.
Ví dụ một bảng băm đơn giản Tìm kiếm trên bảng băm
• Thao tác cơ bản nhất được cung cấp bởi
Với các giá trị: 31, 10, 14, 93, 82, 95,79,18, 27, 46 Hashtable là “tìm kiếm”
• Chi phí tìm kiếm trung bình là O(1), không
phụ thuộc vào số lượng phần tử của mảng
0 1 2 3 4 5 6 7 8 9
(Bảng)
10 31 82 93 14 95 46 27 18 79
• Chi phí tìm kiếm xấu nhất (ít gặp) có thể là
O(n)
Vấn đề xung đột khi xử lý bảng băm a. Làm giảm xung đột
- Trong thực tế có nhiều trường hợp có nhiều -Hàm băm cần thỏa mãn các điều kiện:
hơn 2 phần tử sẽ được “băm” vào cùng 1 vị trí Xác xuất phân bố khoá là đều nhau
Dễ dàng tính toán thao tác
- Hiển nhiên phần tử được “băm” đầu tiên sẽ Ít xảy ra đụng độ
chiếm lĩnh vị trí đó, các phần tử sau cần phải Thông thường, hàm băm sử dụng các số nguyên
được lưu vào các vị trí trống khác sao cho vấn tố (vì xác suất ngẫu nhiên phân bố các số nguyên
đề truy xuất và tìm kiếm phải dễ dàng tố là đều nhất)
Các phương pháp băm: -Ý tưởng: “Các phần tử băm vào trùng vị trí k
PP kết nối trực tiếp được nối vào danh sách nối kết” tại vị trí đó
PP kết nối hợp nhất
0 1 2 3 4
PP dò tuyến tính Hàm băm:
PP dò bậc 2 21 18 F(k) = k mod 5
Thêm
PP băm kép 6, 16 6
16
CTDL2 – Lương Trần Hy Hiến 17 CTDL2 – Lương Trần Hy Hiến 18
PP DSLK có nhiều khuyết điểm: - Sử dụng DSLK tại mỗi vị trí lưu trữ
- Khi có quá nhiều khoá vào cùng vị trí, - Nếu xảy ra xung đột thì lưu vào cuối DS
DSLK thì tại vị trí đó sẽ rất dài => Tăng chi phí tại vị trí trùng
tìm kiếm
- Các ô trống còn dư nhiều => lãng phí về 0 1 2 3 4
Hàm băm:
thời gian tìm kiếm và không gian lưu trữ 21 18 F(k) = k mod 5
Thêm
6, 16 6
Thêm một khoá vào bảng băm Xoá một khoá trong bảng băm
//Chèn một khoá vào bảng băm
void Insert(int k){ //Hàm xóa 1 phần tử có khóa key
InsertTail_LINKLIST(HashTable[Hash(k)], k); void Delete(int key)
} {
//Chèn cuối danh sách
void InsertTail_LINKLIST(LINKLIST &l, int k) Delete_LINKLIST(HashTable[Hash(key)], key);
{ }
//Tạo node
node *add = GetNode(k);
if (l == NULL) l = add;
else
{
node * p = l;
while (p->next != NULL) p = p->next;
p->next = add;
}
} CTDL2 – Lương Trần Hy Hiến CTDL2 – Lương Trần Hy Hiến
27 28
Xoá một khoá trong bảng băm Xoá một khoá trong bảng băm
else{
void Delete_LINKLIST(LINKLIST &l, int key)
{ while((p != NULL)&&(p->key != key))
node *p = l, *q = NULL; {
q = p; p = p->next;
if(p == NULL) //DS rong
}
cout<<endl<<"Khong tim thay - DS rong!"<<endl;
else //tim thay if(p != NULL) //tim thay
{ {
q->next = p->next;
if(p->key==key)//Nam dau danh sach
{ delete p;
l = p->next; cout<<endl<<"Da xoa!"<<endl<<endl;
delete p; }
cout<<endl<<"Da xoa!"<<endl; else
} cout<<"KQ: Khong tim thay"<<endl;
}
}
CTDL2 – Lương Trần Hy Hiến 29 CTDL2 – Lương Trần Hy Hiến 30
}
-Khi xảy ra đụng độ thì chèn vào vị trí trống gần const int M = 101; //Kich thuoc bảng băm
nhất
- Ví dụ: chèn dãy: 5 16 7 8 2 4 6 3 13 24 int HashTable[M]; //bảng băm
vào mãng băm có 11 vị trí
0 1 2 3 4 5 6 7 8 9 10
24 2 3 4 5 16 7 8 6 13
Thêm một khoá vào bảng băm Xoá một khoá trong bảng băm
void Insert(int k)
{ int Delete(int key)
//Xác định vị trí của khoá k {
int pos = Hash(key) int pos = Hash(key); count = 1;
while (HashTable[pos] != key){
//Kiểm tra đảm bảo vị trí pos là trống pos = Hash(pos + 1 );
while (HashTable[pos] != -1 ) count ++;
{ if (count > M) break;
pos = Hash (pos + 1); }
//Kiểm tra full if (count <= M){ HashTable[pos] = -1; return 1;}
}
return 0; //không tìm được khoá
HashTable[pos] = key; }
}
CTDL2 – Lương Trần Hy Hiến 43 CTDL2 – Lương Trần Hy Hiến 44
iv. Sử dụng PP “Dò bậc hai” iv. Sử dụng PP “Băm kép”
f(key)=(f(key) + i2) % M
với f(key) là hàm băm chính của bảng băm. Ta sử dụng 2 hàm băm:
f1(key)= key % M
0 1 2 3 4 f2(key)= (M–2) – key % (M-2)
Hàm băm:
16 21 6 18 F(k) = k mod 5
Thêm
6, 16
CTDL2 – HIENLTH
HIENLTH, HCMUP 2
CTDL2 – HIENLTH
HIENLTH, HCMUP 3 CTDL2 – HIENLTH
HIENLTH, HCMUP 4
Giải quyết Giải quyết
• Thao tác tìm kiếm trên cây tỉ lệ thuận với • Dùng 1 loại cây khác nhằm mục đích
chiều cao của cây. Nếu cây có N nút thì. – Phân trang dữ liệu
– Chiều cao của cây nhị phân tìm kiếm cân • Tăng số nhánh của cây -> Giảm chiều cao của
bằng ≤ log2N = chi phí tìm kiếm. cây
• Gom nhóm dữ liệu thành những block-> giảm
– Cây 2-3-4 (mỗi nút có nhiều nhất 4 khoá) ->
số lần truy xuất trên đĩa
≤ chiều cao log4N.
⇒Dùng B-Cây (1 loại cây nhiều nhánh) thích
– Chưa phù hợp khi lưu trữ và truy xuất trên
đĩa hợp với việc lưu trữ và truy xuất trên bộ nhớ
ngoại - đĩa cứng
CTDL2 – HIENLTH
HIENLTH, HCMUP 5 CTDL2 – HIENLTH
HIENLTH, HCMUP 6
CTDL2 – HIENLTH
HIENLTH, HCMUP 7 CTDL2 – HIENLTH
HIENLTH, HCMUP 8
Chiều cao B-Tree Khai báo CTDL
• N: Số khoá (key), N ≥1 struct NodeType
{
• m: Bậc của cây, m > 2 cấp (m-1)/2 int numtree;//số cây con
int Key[Order];//mảng lưu các khóa của node
N +1 NodeType* Branch[Order+1];//con trỏ trỏ đến
h ≤ log node con
m 2 };
typedef NodeType* NODEPTR;
NODEPTR * Root;//con trỏ trỏ tới nút gốc
CTDL2 – HIENLTH
HIENLTH, HCMUP 9 CTDL2 – HIENLTH
HIENLTH, HCMUP 10
Các phép toán trên cây Tìm kiếm phần tử có khóa X trên cây
• Tìm 1 phần tử có khóa bằng X trong cây • Việc tìm kiếm được thực hiện tuần tự giống
như trong cây nhị phân tìm kiếm:
• Thêm 1 khoá vào vào B –Tree – Bắt đầu từ gốc.
– Duyệt cây theo kiểu top-down.
• Xóa 1 khoá trong 1 nút – Tại mỗi node so sánh khóa cần tìm với các
giá trị khóa của node đó để tìm nhánh con
cần duyệt tiếp theo.
• Có thể áp dụng phương pháp tìm kiếm nhị
phân để tìm một khóa trong nội bộ một
node.
CTDL2 – HIENLTH
HIENLTH, HCMUP 11 CTDL2 – HIENLTH
HIENLTH, HCMUP 12
Thêm 1 nút vào B-Tree Thêm 1 nút vào cây B-Tree
• Tính chất cây B (B-tree): một khối có ít • Nếu số khóa lớn hơn 2n thì tách trang:
nhất một nữa số khóa – Đưa phần tử giữa lên trang cha
• Thêm 1 nút có khóa X vào B-Tree – Tạo thêm trang mới
– Thêm X vào 1 nút lá
– Chuyển dời một nửa phần tử sang trang mới
– Sau khi thêm, nếu nút lá đầy:
• Tách nút lá ra làm đôi – Tiếp tục lan truyền ở trang cha (nếu trang cha
• Chuyển phần tử giữa lên nút cha và lan sau khi thêm > 2n phần tử thì thực hiện tách
truyền ngược về gốc. trang như trên).
• Nếu gốc bị tách, cây được đặt ở mức sâu
hơn
CTDL2 – HIENLTH
HIENLTH, HCMUP 13 CTDL2 – HIENLTH
HIENLTH, HCMUP 14
– Chèn 9
– Node bị tràn,
phân tách nó
– Đẩy node giữa (8)
– Gốc bị tràn,
phân tách nó
– Đẩy node giữa (6)
– Node gốc mới hình thành
– Chiều cao tăng 1
CTDL2 – HIENLTH
HIENLTH, HCMUP 15 CTDL2 – HIENLTH
HIENLTH, HCMUP 16
Ví dụ thao tác thêm (B-Tree: n = 2) Ví dụ thao tác thêm (B-Tree: n = 2)
• Bốn phần tử đầu tiên 1 2 8 12
• Cho B-tree rỗng. Lần lượt thêm giá trị các được đưa vào nút gốc
khóa sau (theo thứ tự) vào B-tree: 1, 12, 8, • Nếu đưa phần tử thứ năm 8
2, 25, 5, 14, 28, 17, 7, 52, 16, 48, 68, 3, vào nút gốc sẽ làm vi
phạm điều kiện cây B- 1 2 12 25
26, 29, 53, 55, 45. tree.
• Cần xây dựng B-tree bậc 2. • Do đó, khi thêm khóa 25,
tách nút gốc thành 2 nút
và đưa khóa ở giữa lên để
tạo nút gốc mới.
CTDL2 – HIENLTH
HIENLTH, HCMUP 17 CTDL2 – HIENLTH
HIENLTH, HCMUP 18
Nút lá bên phải đã đầy Thêm khóa 68 vào, cần tách nút lá ở bên phải, đưa 48
(đã có 4 phần tử). Do đó, 8 17 lên nút gốc
khi thêm khóa 17 vào nút
lá bên phải sẽ làm nút lá Thêm khóa 3 vào, cần tách nút lá ở bên trái, đưa khóa
bị “quá tải”. Lấy khóa ở
1 2 6 12 14 25 28 3 lên nút gốc
3 8 17 48
giữa đưa lên nút cha
(hiện tại là nút gốc) và
phân chia nút hiện tại. 1 2 6 7 12 14 16 25 28 52 68
CTDL2 – HIENLTH
HIENLTH, HCMUP 19 CTDL2 – HIENLTH
HIENLTH, HCMUP 20
Ví dụ thao tác thêm (B-Tree: n = 2) Xóa 1 phần tử trên B-Cây bậc n
Thêm các khóa 26, 29, 53, 55 vào các nút lá • Khóa cần xóa trên trang lá Xóa bình
3 8 17 48 thường.
• Khóa cần hủy không trên trang lá:
1 2 6 7 12 14 16 25 26 28 29 52 53 55 68 – Tìm phần tử thay thế: Trái nhất (hoặc phải nhất)
trên hai cây con cần tìm
Thêm khóa 45 vào, cần tách 25 26 28 29 và đưa khóa 28
lên nút gốc. Khi đó, nút gốc sẽ “quá tải” và cần tách tiếp. – Thay thế cho nút cần xóa
17 • Sau khi xóa, trang bị thiếu (vi phạm đk B-
Tree):
3 8 28 48
– Hoặc chuyển dời phần tử từ trang thừa
1 2 6 7 12 14 16 25 26 29 45 52 53 55 68 – Hoặc ghép với trang bên cạnh (trái/phải)
CTDL2 – HIENLTH
HIENLTH, HCMUP 21 CTDL2 – HIENLTH
HIENLTH, HCMUP 22
Ví dụ về xóa Ví dụ về xóa
• Giả sử đã xây dựng B-cây như sau: • Xóa 15:
12 24 37 12 24 37
7 9 15 17 20 22 28 32 42 44 48 7 9 17 20 22 28 32 42 44
7 9 15 17 20 22 28 32 42 44
7 9 17 20 22 28 32 37 42
CTDL2 – HIENLTH
HIENLTH, HCMUP 23 CTDL2 – HIENLTH
HIENLTH, HCMUP 24
Ví dụ về xóa Ví dụ về xóa
• Xóa 7 (mượn trang phải) • Xóa 20: Mượn trang phải 1 phần tử. Tức mang 32
17 24 lên cha, 28 xuống nút có 1 khóa là 22
9 12 20 22 28 32 37 42
9 12 20 22 32 37 42
CTDL2 – HIENLTH
HIENLTH, HCMUP 25 CTDL2 – HIENLTH
HIENLTH, HCMUP 26
Ví dụ xóa nút trong B-Tree (tt) Ví dụ xóa nút trong B-Tree (tt)
• Xóa 28: 17
• Xóa 37 22
9 12 22 32 37 42 12 17 32 42
• Xóa 9 22 • Xóa 17
12 22 32 42
12 17 32 37 42
CTDL2 – HIENLTH
HIENLTH, HCMUP 27 CTDL2 – HIENLTH
HIENLTH, HCMUP 28
B-tree: Cân bằng lại cây sau khi xóa Trường hợp:
• Nếu một trong các nút anh em kế cận nút đang xét
Nút anh em kế cận còn đủ khóa để bổ sung
có số lượng khóa nhiều hơn số lượng tối thiểu
12 29
– Đưa một khóa của nút anh em lên nút cha.
Delete 22 Đưa khóa từ nút cha xuống
– Đưa một khóa ở nút cha xuống nút đang xét. Đưa khóa từ nút anh em kế cận lên
• Nếu tất cả các nút anh em kế cận nút đang xét đều
7 9 15 22 31 43 56 69
có số lượng khóa vừa đủ số lượng tối thiểu.
– Chọn một nút anh em kế cận và hợp nhất nút anh
em này với nút đang xét và với khóa tương ứng ở 12 31
nút cha.
– Nếu nút cha trở nên thiếu khóa, lặp lại quá trình
này.
7 9 15 29 43 56 69
CTDL2 – HIENLTH
HIENLTH, HCMUP 29 CTDL2 – HIENLTH
HIENLTH, HCMUP 30
Hợp nhất
7 9 15 22 31 43 69 72
• Xoá nút 26 thì ta
làm sao:
Còn quá
ít khóa
Delete 72
12 29
7 9 15CTDL2
22 – HIENLTH
31 43 56
HIENLTH, 69
HCMUP 31 CTDL2 – HIENLTH
HIENLTH, HCMUP 32
VD 2 – Xoá 1 khoá trong B-Tree (tt) VD 2 – Xoá 1 khoá trong B-Tree (tt)
• Dùng 28 để thay thế • Xoá khóa 22 dùng 24 thay thế, thiết nút lá
CTDL2 – HIENLTH
HIENLTH, HCMUP 33 CTDL2 – HIENLTH
HIENLTH, HCMUP 34
CTDL2 – HIENLTH
HIENLTH, HCMUP 35 CTDL2 – HIENLTH
HIENLTH, HCMUP 36
VD 2 – Xoá 1 khoá trong B-Tree (tt) Cây B trên đĩa
• Các khối đĩa
– 512 - 8k bytes
∴100s of keys
Dùng tìm kiếm nhị phân cho các khối
• Tổng quát
– O( log n )
– Làm hợp với phần cứng !
• Thủ tục xóa tương tự (Deletion)
– Tuy nhiên, phải hội nhập các khối (block) để bảo đảm
tính chất B-tree
(ít nhất bằng nửa số lượng khóa)
CTDL2 – HIENLTH
HIENLTH, HCMUP 37 CTDL2 – HIENLTH
HIENLTH, HCMUP 38
Ví dụ Ví dụ
CTDL2 – HIENLTH
HIENLTH, HCMUP 39 CTDL2 – HIENLTH
HIENLTH, HCMUP 40
Cây B(Block) (B-trees) Cây B+
• Tất cả các lá nằm trên cùng một mức • Cây B+
• Tất cả các node ngoại trừ gốc và các lá có: – Tất cả các khóa trong các node là giả
– Ít nhất m/2 con Mỗi node chứa ít nhất – Chỉ các khóa trong các lá nhận giá trị thực “real”
– Nhiều nhất m con một nửa số lượng khóa
– Các bản ghi dữ liệu được giữ trong các vùng
• Cây B+ (B+ trees) riêng
– Tất cả các khóa trong các node là giả
– Chỉ các khóa trong các lá nhận giá trị thực “real”
– Liên kết các lá
• Có khả năng duyệt hết danh sách theo thứ tự giữa
không cần thông qua các node cao hơn.
CTDL2 – HIENLTH
HIENLTH, HCMUP 41 CTDL2 – HIENLTH
HIENLTH, HCMUP 42
CTDL2 – HIENLTH
HIENLTH, HCMUP 43 CTDL2 – HIENLTH
HIENLTH, HCMUP 44
Đọc thêm Luyện tập
• Tài liệu tham khảo • Cho B-tree bậc 2 gồm các khóa sau (chèn
– http://en.wikipedia.org/wiki/B-tree vào theo thứ tự):
– http://www.bluerwhite.org/btree/ 3, 7, 9, 23, 45, 1, 5, 14, 25, 24, 13, 11,
– http://www.nist.gov/dads/HTML/btr 8, 19, 4, 31, 35, 56
ee.html
– http://www.nist.gov/dads/HTML/bs • Thêm khóa: 2, 6, 12, 0, 10, 11
tartree.html
• Xóa khóa: 4, 5, 7, 3, 14
CTDL2 – HIENLTH
HIENLTH, HCMUP 45 CTDL2 – HIENLTH
HIENLTH, HCMUP 46
CTDL2 – HIENLTH
HIENLTH, HCMUP 47
Đại Học Sư Phạm Tp. Hồ Chí Minh
Ôn tập cây nhị phân
• Xem lại các kiến thức liên quan đến cây nhị
phân [tìm kiếm [cân bằng]]]
CẤU TRÚC DỮ LIỆU 2 • Độ cao của cây nhị phân tìm kiếm cân bằng
N node là O(logN)
• Cây nhị phân cân bằng có chi phí thấp nhất.
• Các loại cây tìm kiếm cân bằng:
– AVL Tree
– Red – Black Tree
Chương 04: CÂY ĐỎ ĐEN – AA Tree
– Splay Tree
CTDL2 – HIENLTH
HIENLTH, HCMUP 2
CTDL2 – HIENLTH
HIENLTH, HCMUP 3 CTDL2 – HIENLTH
HIENLTH, HCMUP 4
Ví dụ: Cây đỏ đen
• Chiều cao đen (black height – hb(x)) là số
node đen trên đường đi từ node x đến lá
(không bao gồm x)
• Từ quy tắc [4] Không thể tồn tại node cha
và node con cùng đỏ. Khi cây đỏ đen vi
phạm quy tắc này gọi là hiện tượng xung đột
đỏ - đỏ.
CTDL2 – HIENLTH
HIENLTH, HCMUP 5 CTDL2 – HIENLTH
HIENLTH, HCMUP 6
CTDL2 – HIENLTH
HIENLTH, HCMUP 11 CTDL2 – HIENLTH
HIENLTH, HCMUP 12
d. Xuất cây ra màn hình (tt) e. Chèn một nút vào cây đỏ đen
Print(p->pRight, level + 1);
for (i= 0; i < level;i++) Chèn một nút như trong cây tìm kiếm nhị phân
cout<<“ “; bình thường và gán cho nó màu đỏ.
if (p->color == RED)
textcolor (RED); B1. Tìm vị trí thích hợp để chèn khoá K
else B2. Phát sinh 1 nút mới có khoá K, màu
textcolor (BLACK); đỏ (RED) và gắn liên kết tại vị trí tìm
cout<<p->Key; được.
Print(p->pLeft , level +1) B3. Hiệu chỉnh lại cây nếu có vi phạm
}
tính chất (tô màu lại & quay)
}
CTDL2 – HIENLTH
HIENLTH, HCMUP 13 CTDL2 – HIENLTH
HIENLTH, HCMUP 14
U
CTDL2 – HIENLTH
HIENLTH, HCMUP 17 U CTDL2 – HIENLTH
HIENLTH, HCMUP 18
y y
Quay phải
x
γ γ
α
x x Quay trái
y
α β α β β γ
CTDL2 – HIENLTH
HIENLTH, HCMUP 19 CTDL2 – HIENLTH
HIENLTH, HCMUP 20
Các trường hợp sau khi thêm: Trường hợp 1
• Nếu p đen Dừng (1) • P đen Bình thường (dừng)
• Nếu p đỏ:
– Nếu u đỏ, đảo màu 3 nút p, u, g (2)
– Nếu u đen:
• Nếu x là cháu ngoại của g: Thực hiện 1
phép quay (3)
• Nếu x là cháu nội của g: Thực hiện 2
phép quay (4)
CTDL2 – HIENLTH
HIENLTH, HCMUP 21 CTDL2 – HIENLTH
HIENLTH, HCMUP 22
Trường hợp 2
G
• P đỏ, U đỏ Đảo màu G, P, U
P X
U
G
g g X
P U
p u p u
Case 2 – P, U is Red
x x Just Recolor and move up
CTDL2 – HIENLTH
HIENLTH, HCMUP 23 CTDL2 – HIENLTH
HIENLTH, HCMUP 24
Trường hợp 3
G
• P đỏ, U đen, x là cháu ngoại Quay tại
P; Đổi màu P, G P U
S P
X
X G
Case 3 – Zig-Zig
Single Rotate P around G
S U
Recolor P and G
CTDL2 – HIENLTH
HIENLTH, HCMUP 25 CTDL2 – HIENLTH
HIENLTH, HCMUP 26
Trường hợp 4
G
• P đỏ, U đen, x là cháu nội Quay tại P
(trở thành cháu ngoại), sau đó Quay tại P U
G; Đổi màu P, G
S X X
Tương tự P G
TH3
Case 4 – Zig-Zag
Double Rotate S
X around P; X around G
U
Recolor G and X
CTDL2 – HIENLTH
HIENLTH, HCMUP 27 CTDL2 – HIENLTH
HIENLTH, HCMUP 28
Hàm quay trái (tương tự cho quay phải) Hàm quay trái (tt)
void LeftRotate(RBTree t, pNode x){ //trường hợp else => x không phải là gốc
pNode y = x->pRight;
x->pRight = y->pLeft; //Bên phải x là β //Nếu x là con trái của cha nó
if (x == (x->pParent)->pLeft)
if (p->pLeft !=NULL) x->pParent->pLeft = y
p->pLeft ->pParent = x; //cha β là x else
p->pParent = x->pParent; // cha của x là cha của y x->pParent->pRight = y;
Ví dụ: Chèn một nút vào cây đỏ đen TH2: Cha, bác x đều có màu đỏ
Lần lượt chèn vào các nút có giá trị: - Cho cha và bác thành màu đen
50, 75, 25, 80, 100, 110, 105 -Ông của x thành màu đỏ
(lưu ý: nút gốc qui ước là màu đen)
50
“ông” x 50 “ông” x
“bác” x 75
“cha” x “bác” x 75
“cha” x
25 25
x x
80 80
CTDL2 – HIENLTH
HIENLTH, HCMUP 31 CTDL2 – HIENLTH
HIENLTH, HCMUP 32
TH3: Bác x đều có màu đen x là con phải của cha x
75 100
80
25 75
25
80
100
x 110
75
100 x 105
x
CTDL2 – HIENLTH
HIENLTH, HCMUP 33 CTDL2 – HIENLTH
HIENLTH, HCMUP 34
{
//Làm tương tự như trên,đổi pRight<->pLeft 1 7 15
}
t.root -> color = BLACK; 5 8
CTDL2 – HIENLTH
HIENLTH, HCMUP 39 CTDL2 – HIENLTH
HIENLTH, HCMUP 40
Red-Black Trees Red-Black Trees
Insertion example Insertion example
11 11
2 14 7 14
15 2 8 15
1 7
1 5
5 8
4 Case 1 4 Case 2
CTDL2 – HIENLTH
HIENLTH, HCMUP 41 CTDL2 – HIENLTH
HIENLTH, HCMUP 42
Red-Black Trees
Insertion example
2 11
1 5 8 14
15
4
Case 3
CTDL2 – HIENLTH
HIENLTH, HCMUP 43 CTDL2 – HIENLTH
HIENLTH, HCMUP 44
f. Huỷ một nút trong cây đỏ đen
• Tìm và hủy giống cây NPTK (BST).
• Kiểm tra tính cân bằng mất cân bằng
cân bằng lại.
Sau khi xóa:
• Nếu y đỏ Không cần cân bằng lại.
• Nếu y đen:
– Tất cả các đường đi con từ gốc đến nút lá (qua
y) sẽ ít hơn 1 nút đen so với những đường đi
còn lại (vi phạm t/c 4)
– Nếu p và x cùng đỏ thì vi phạm t/c 3.
CTDL2 – HIENLTH
HIENLTH, HCMUP 45 CTDL2 – HIENLTH
HIENLTH, HCMUP 46
CTDL2 – HIENLTH
HIENLTH, HCMUP 47 CTDL2 – HIENLTH
HIENLTH, HCMUP 48
Quy ước Quy ước
CTDL2 – HIENLTH
HIENLTH, HCMUP 49 CTDL2 – HIENLTH
HIENLTH, HCMUP 50
CTDL2 – HIENLTH
HIENLTH, HCMUP 51 CTDL2 – HIENLTH
HIENLTH, HCMUP 52
CODE CODE
void Delete(RBTree &t, int k)
{
//Tìm nút z cần xoá Xác định nút y là nút cần thực sự xoá
if (isEmpty(t)) return; If (z->pLeft ==Null !! z->pRight == Null)
pNode z = t.root;
while (z!=Null && z->key !=k){
y = z; //z không đủ 2 con => xoá
if (z->key > k) else
z= z->pLeft; //y chỉ có thể có 1 con
else
z= z->pRight;
y = SearchStandFor(z);
}
if (z==Null)
return ; //Không tìm thấy
CTDL2 – HIENLTH
HIENLTH, HCMUP 53 CTDL2 – HIENLTH
HIENLTH, HCMUP 54
CODE CODE
//Tiến hành xoá y
pNode x; // x là nút con của y if (y->pParent == Null) //y là gốc
if (y->pLeft != Null) t.root = x;
x=y->pLeft; else
else if (y->Parent->pLeft ==y)
x = y->pRight; y->pParent->pLeft = x;
else
//cha của x là cha của y y->pParent->pRight =x;
//Nếu x là Null vẫn không báo lỗi
x->pParent = y->pParent;
CTDL2 – HIENLTH
HIENLTH, HCMUP 55 CTDL2 – HIENLTH
HIENLTH, HCMUP 56
CODE Cân bằng lại cây sau khi xoá một nút đen
//Kiểm tra cân bằng
if (y != z) // y là phần tử thế mạng x: con trỏ, trỏ tới nút đang thiếu đen
z->key = y->key; w: con trỏ, trỏ tới nút anh/em của x
if (y->color == BLACK) p: con trỏ, trỏ tới nút cha của x
FIXUP(t,x); // cân bằng lại
//Xoá y
y->pLeft = y->pRight = y->pParent=Null;
Delete y;
CTDL2 – HIENLTH
HIENLTH, HCMUP 57 CTDL2 – HIENLTH
HIENLTH, HCMUP 58
CTDL2 – HIENLTH
HIENLTH, HCMUP 59 CTDL2 – HIENLTH
HIENLTH, HCMUP 60
TH2: x đen kép, w và 2 con w đều đen TH3: x đen kép, w đỏ, 2 con w đen, p đen
x
B
w Đổi màu B • Đảo màu nút p và w
w sang đỏ
A D
A D • Thực hiện phép quay tại w (theo hướng
α β α
đưa w lên, p xuống w làm ông của x)
C E β
C E
γ ε η • Dấu hiệu đen vẫn chỉ vào nút x ban đầu
δ δ γ ε η
B B p w
x w Đổi màu
w sang đỏ Trở về một trong các
A D A D x w p trường hợp trên.
α β α β
C E C x
E
δ γ ε η δ γ ε η
CTDL2 – HIENLTH
HIENLTH, HCMUP 61 CTDL2 – HIENLTH
HIENLTH, HCMUP 62
CTDL2 – HIENLTH
HIENLTH, HCMUP 63 CTDL2 – HIENLTH
HIENLTH, HCMUP 64
TH4a: w đen con trái w màu đỏ Xử lý
• 4a) chuyển sang 4b):
Đỏ/đen – Đổi màu Z và W
BB
B – Quay tại Z (theo hướng đưa z lên, w xuống)
Quay phải tại W w mới
x x C • Xử lý 4b)
w D Đổi màu A
– Quay tại W (theo hướng đưa W lên, P xuống)
δ
A
z z mới – Đảo màu P, W
β
D
α β E
α – Z đổi theo màu P
γ
C
E – Mất dấu hiệu đen
δ γ ε η
ε η
CTDL2 – HIENLTH
HIENLTH, HCMUP 65 CTDL2 – HIENLTH
HIENLTH, HCMUP 66
ε η 55 65
α β C E z A C
Lần lượt xóa các nút sau: 55, 50.
δ γ ε η γ
α β δ
CTDL2 – HIENLTH
HIENLTH, HCMUP 67 CTDL2 – HIENLTH
HIENLTH, HCMUP 68
Cây 2-3-4 Cây 2-3-4
Node không phải là lá:
+ Cây 2-3-4 là cây nhiều nhánh mà mỗi node của • Một node với một mục dữ liệu thì luôn
nó có thể có đến bốn node con và ba mục dữ liệu. luôn có 2 con.
• Một node với hai mục dữ liệu thì luôn
luôn có 3 con.
• Một node với ba mục dữ liệu thì luôn
luôn có 4 con.
Đối với mọi node với số con là k và số
mục dữ liệu là d, thì : k = d + 1
CTDL2 – HIENLTH
HIENLTH, HCMUP 69 CTDL2 – HIENLTH
HIENLTH, HCMUP 70
CTDL2 – HIENLTH
HIENLTH, HCMUP 71 CTDL2 – HIENLTH
HIENLTH, HCMUP 72
Các trường hợp của cây 2-3-4 Các trường hợp của cây 2-3-4
2 - 3 - 4 tree
CTDL2 – HIENLTH
HIENLTH, HCMUP 73 CTDL2 – HIENLTH
HIENLTH, HCMUP 74
CTDL2 – HIENLTH
HIENLTH, HCMUP 75 CTDL2 – HIENLTH
HIENLTH, HCMUP 76
Cách chèn nút Thêm nút
• Theo nguyên lý Top – Down (đi từ trên
+ Bottom – up: Mở rộng cây về phía gốc, xuống), gặp nút đầy thì tách rồi mới chèn.
tách từ dưới lên, tràn node => tách • Dữ liệu thêm luôn được chèn vào node lá.
+ Top – Down: đi từ trên xuống, gặp nút • Nếu node thêm vào chưa đầy thêm vào
đầy thì tách, rồi tìm vị trí thích hợp để chèn bình thường.
• Node đầy dữ liệu (có 3 mục dữ liệu) thì tách.
Ví dụ: (xem demo trên web) Ta có 2 trường hợp:
– Tách node gốc
– Tách node không phải là gốc
CTDL2 – HIENLTH
HIENLTH, HCMUP 77 CTDL2 – HIENLTH
HIENLTH, HCMUP 78
CTDL2 – HIENLTH
HIENLTH, HCMUP 79 CTDL2 – HIENLTH
HIENLTH, HCMUP 80
Ví dụ Tách nút Ví dụ Tách nút
CTDL2 – HIENLTH
HIENLTH, HCMUP 81 CTDL2 – HIENLTH
HIENLTH, HCMUP 82
CTDL2 – HIENLTH
HIENLTH, HCMUP 83 CTDL2 – HIENLTH
HIENLTH, HCMUP 84
Ví dụ về xóa node Ví dụ về xóa node
• Khóa k nằm trong nút lá u và u chỉ có một khóa và • Khóa k nằm trong nút lá u và u chỉ có một khóa và
tồn tại nút anh em v của u có nhiều hơn một khóa tất cả các nút anh em của u chỉ có một khóa thì
thì bằng phép dịch chuyển dần có thể dịch chuyển bằng phép gộp u với nút anh em kề nó sẽ khiến u
một khóa của v đến u khiến u trở thành 3-nút và trở thành 3-nút và quay về trường hợp 1.
quay về trường hợp 1.
CTDL2 – HIENLTH
HIENLTH, HCMUP 85 CTDL2 – HIENLTH
HIENLTH, HCMUP 86
CTDL2 – HIENLTH
HIENLTH, HCMUP 87 CTDL2 – HIENLTH
HIENLTH, HCMUP 88
Biến đổi cây 2-3-4 sang cây đỏ đen Biến đổi cây 2-3-4 sang cây đỏ đen
• Biến đổi bất kỳ 2-node ở cây 2-3-4 sang • Biến đổi bất kỳ 3-node sang node con C (với
node đen ở cây đỏ-đen. hai con của chính nó) và node cha P (với các
node con C và node con khác). C được tô
màu đỏ và P được tô màu đen
Biến đổi cây 2-3-4 sang cây đỏ đen Câu hỏi và thảo luận
• Biến đổi bất kỳ 4-node sang node cha P
và cả hai node con C1, C2 màu đỏ.