You are on page 1of 68

BỘ GIÁO DỤC VÀ ĐÀO TẠO

TRƯỜNG ĐẠI HỌC SƯ PHẠM TP.HCM


Khoa Toán – Tin học
--------------

Slide bài giảng

CẤU TRÚC DỮ LIỆU 2

Giảng viên: Lương Trần Hy Hiến


Email: hienlth@hcmup.edu.vn

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

Thông tin môn học Mục tiêu


• CẤU TRÚC DỮ LIỆU 2 • Nhằm giúp sinh viên tiếp cận các cấu trúc dữ
• Số đvht: 4(CQ) – 3(CT) liệu cao cấp.
• Điều kiện tiên quyết: • Hiểu được tầm quan trọng của giải thuật và
– Cấu trúc dữ liệu 1 cách tổ chức chương trình và khai thác các
– Đã học môn “Lập trình C++” dữ liệu phức tạp.
– Kiến thức cơ bản về lập trình • Giúp sinh viên củng cố và phát triển kỹ năng
phân tích và lập trình ở mức độ cao.

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

Đề tài môn học Tài liệu tham khảo


• Xây dựng chương trình mô tả cây đỏ • Slide bài giảng của giảng viên
đen, B-cây, cây 2-3-4 trong đó mô tả
• Algorithms, Robert Sedgewick.
chi tiết CTDL đã dùng, các thao tác liên
quan tới cây. • Algorithms + Data Structures = Program,
• Tìm hiểu R-Tree, RBTree, B+ Tree, B++
N.Wirth.
Tree, KD-Tree, SBB-Tree
• Tìm hiểu các giải thuật nén RLE, • Các tài liệu tham khảo trên Internet
Huffman, LZ77, LZ78
• Tìm hiểu mã hóa dữ liệu với DES, AES,
RSA
7 8
Đại Học Sư Phạm Tp. Hồ Chí Minh
Đề cương môn học: CTDL2
• Chương 1: Sắp xếp ngoại
• Chương 2: Bảng băm (Hash Table)
CẤU TRÚC DỮ LIỆU 2 • Chương 3: Cây 2-3-4, B – cây
• Chương 4:Cây Đỏ Đen (Red Black Tree)

Chương 01: SẮP XẾP NGOẠI

CTDL2 – Lương Trần Hy Hiến 2

Giới thiệu Chương 1: SẮP XẾP NGOẠI


• Mục tiêu: • Phương pháp trộn Run
– Bài toán kinh điển: Sắp xếp và tìm kiếm • Phương pháp trộn tự nhiên
– Đặc trưng của SX trên file: Bài toán trộn • Phương pháp trộn đa lối cân bằng
– TT tìm kiếm cơ bản: Tuần tự, nhị phân
• Phương pháp trộn đa pha
– TT sắp xếp: Trộn trực tiếp, trộn tự nhiên, trộn n-
đường cân bằng, trộn đa pha
– Đánh giá thuật toán
• Các nội dung chính:
– Thuật toán và cài đặt các phương pháp sắp xếp

CTDL2 – Lương Trần Hy Hiến 3 CTDL2 – Lương Trần Hy Hiến 4


Các PP sắp xếp – Vấn đề??? Bài toán trộn
Vì sao phải xây dựng thuật toán sắp xếp trên • Cho trước 2 (hay nhiều) dãy đã sắp thứ tự:
file? A[1], A[2], A[3], …, A[N]
• Thao tác cơ bản trong SX là hoán vị B[1], B[2], B[3], …, B[M]
• Thực hiện hoán vị trên cùng 1 file  Tần • Kết hợp các dãy đã cho thành một dãy mới C
suất thao tác lên trên 1 vùng đĩa quá lớn được sắp.
 Không an toàn.
• SX ngoại không thích hợp với hoán vị  Sử
dụng phương pháp trộn.

CTDL2 – Lương Trần Hy Hiến 5 CTDL2 – Lương Trần Hy Hiến 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.

CTDL2 – Lương Trần Hy Hiến 9 CTDL2 – Lương Trần Hy Hiến 10

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

CTDL2 – Lương Trần Hy Hiến 11 CTDL2 – Lương Trần Hy Hiến 12


1. Phương pháp trộn trực tiếp 1. Phương pháp trộn trực tiếp
• Bước 3: • Bước 4: Phân bố m=2 * m = 8 phần tử lần
– Tương tự bước 2, phân bố m= 2 * m = 4 phần lượt từ f0 vào f1, f2.
tử từ f0 vào f1, f2 f1: 11 12 24 33 34 42 58 67
f0: 12 24 33 67 11 34 42 58 29 31 f2: 29 31
f1: 12 24 33 67 29 31
– Trộn f1, f2 thành f0:
f2: 11 34 42 58
f0: 11 12 24 29 31 33 34 42 58 67
– Trộn f1, f2 thành f0:
f0: 11 12 24 33 34 42 58 67 29 31 • Bước 5: Lặp lại tương tự các bước trên cho
tới khi chiều dài m của run cần phân bố lớn
hơn chiều dài n của f0 thì dừng.

CTDL2 – Lương Trần Hy Hiến 13 CTDL2 – Lương Trần Hy Hiến 14

Cài đặt thuật toán Đánh giá


m=1 • Đánh giá:
– Cần ít nhất N không gian trống trên đĩa để hoạt động.
while (m < số phần tử của f0) – Số bước log2N (vì mỗi lần xử lý 1 dãy tăng gấp 2)
{ – Mỗi bước:
• Distribute: Copy N lần
Chia[Distribute] m phần tử của f0 lần lượt • Merge: Copy N lần, so sánh N/2 lần
cho f1, f2 – Tổng cộng:
Trộn[Merge] f1, f2 lần lượt vào f0 • Copy: 2N * log2N
• So sánh: N/2 * log2N
M=M*2
• Hạn chế:
} – Không tận dụng được dữ liệu đã được sắp bộ phận
– Độ dài dãy con xử lý ở bước k <= 2k

CTDL2 – Lương Trần Hy Hiến 15 CTDL2 – Lương Trần Hy Hiến 16


2. Phương pháp trộn tự nhiên 2. Phương pháp trộn tự nhiên
Giải thuật: Giải thuật:
Trong khi số Run của F0 > 1
• Trong phương pháp trộn ở mục 1, giải thuật Phân bố F0 vào F1, F2 theo các Run tự nhiên.
chưa tận dụng được chiều dài cực đại của Trộn các Run của F1, F2 vào F0.
các Run trước khi phân bố  chưa tối ưu. Hết trong khi
do {
• Đặc điểm của PP trộn tự nhiên là tận dụng - [Distribute] Chia xoay vòng dữ liệu của file F0 cho
chiều dài “tự nhiên” của các Run ban đầu; F1 và F2, mỗi lần 1 run cho đến khi file F0 hết
nghĩa là thực hiện việc trộn các Run có độ nDemRunF0 = 0;
dài cực đại với nhau cho tới khi dãy chỉ còn - [Merge] Trộn từng cặp run của F1 và F2 tạo thành
run mới trên F0
1 Run duy nhất  dãy đã được sắp. } while (nDemRunF0 > 1);
CTDL2 – Lương Trần Hy Hiến 17 CTDL2 – Lương Trần Hy Hiến 18

2. Phương pháp trộn tự nhiên 2. Phương pháp trộn tự nhiên


• Ví dụ: F0: 1 2 9 8 7 6 5 • Ví dụ(tt)
• Bước 1: • Bước 3:F0: 1 2 6 7 8 9 5
– F1: 1 2 9 7 5 – F1: 1 2 6 7 8 9
– F2: 8 6 – F2: 5
– F0: 1 2 8 9 6 7 5 – F0: 1 2 5 6 7 8 9
• Bước 2: • Bước 4: Dừng vì F0 chỉ có 1 Run.
– F1: 1 2 8 9 5
– F2: 6 7
– F0: 1 2 6 7 8 9 5
CTDL2 – Lương Trần Hy Hiến 19 CTDL2 – Lương Trần Hy Hiến 20
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
• Thuật toán sắp xếp ngoài cần 2 giai đoạn: • Chi phí sắp xếp ngoài tỉ lệ với số bước thực hiện:
– Nếu mỗi bước cần N thao tác copy
Phân phối và trộn. – Nếu dùng 2 file trung gian cần log2N bước  cần N *
– Giai đoạn nào làm thay đổi thứ tự? log2N thao tác copy.
– Để giảm số bước  Phân bố số Run nhiều hơn 2 file
– Chi phí cho giai đoạn phân phối? trung gian.
– Nếu dùng n file trung gian: cần lognN bước  cần N *
• Rút ra kết luận: lognN thao tác copy.
– Thay vì thực hiện 2 giai đoạn, ta chỉ cần thực • Rút ra kết luận:
hiện 01 giai đoạn trộn. – Dùng nhiều file trung gian để giảm số bước.
• Tiết kiệm ½ chi phí Copy. – Tiết kiệm thao tác copy bằng cách thực hiện 1 giai đoạn
– Sử dụng 2*n file trung gian:
• Cần số lượng file trung gian gấp đôi. • n file nguồn
• n file đích

CTDL2 – Lương Trần Hy Hiến 21 CTDL2 – Lương Trần Hy Hiến 22

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]
}

CTDL2 – Lương Trần Hy Hiến 29 CTDL2 – Lương Trần Hy Hiến 30

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

CTDL2 – Lương Trần Hy Hiến 33 CTDL2 – Lương Trần Hy Hiến 34

Bước 2 4. Phương pháp trộn đa pha


g1 2 3 5 7 12 • Phương pháp trộn đa lối cân bằng các tập tin
chưa được sử dụng một cách có hiệu quả bởi
1 2 46 78 15
8 18
15 20 21 23 27
g2
vì trong cùng một lần duyệt thì phân nửa số
g3 tập tin luôn luôn giữ vai trò trộn (nguồn) và
phân nửa số tập tin luôn luôn giữ vai trò
phân phối (đích)  Cải tiến: Thay đổi vai trò
1 2 2 3 4 5 7 7 8 8 12 15 20 21 23 27
f1
của các tập tin trong cùng một lần duyệt 
f2 phương pháp trộn đa pha.
f3

CTDL2 – Lương Trần Hy Hiến 35 CTDL2 – Lương Trần Hy Hiến 36


4. Phương pháp trộn đa pha 4. Phương pháp trộn đa pha
Ta xét ví dụ sau với 3 tập tin f1, f2, f3 • Ví dụ: Trường hợp n = 7, tổng số run ban đầu là
• Bước 1: Phân phối luân phiên các run ban đầu của f0 vào 13 + 8 = 21 run
f1 và f2
• Bước 2: Trộn các run của f1, f2 vào f3 . Giải thuật kết thúc Pharse F1 F2 F3
nếu f3 chỉ có một run
0 1,1,1,1,1,1,1,1 1,1,1,1,1 Sort
• Bước 3: Chép nửa run của f3 vào f1
• Bước 4: Trộn các run của f1 và f3 vào f2. Giải thuật kết 1 1,1,1 2,2,2,2,2 Merge1
thúc nếu f2 chỉ có một run.
• Bước 5: Chép nửa số run của f2 vào f1. Lặp lại bước 2. 2 3,3,3 2,2 Merge2
Phương pháp này còn có nhược điểm là mất thời gian sao 3 5,5 3 Merge3
chép nửa số run của tập tin này vào tập tin kia. Việc sao
chép này có thể loại bỏ nếu ta bắt đầu với Fn run của tập 4 5 8 Merge4
tin f1 và fn-1 run của tập tin f2, với fn và fn-1 là các số liên 5 13 Merge4
tiếp trong dãy Fibonaci.
6 21 Merge6
CTDL2 – Lương Trần Hy Hiến 37 CTDL2 – Lương Trần Hy Hiến 38

4. Phương pháp trộn đa pha


• Phase 0: Phân phối các run ban đầu
• Phase 1: Trộn 8 run của f1 và f2 vào f3
• Phase 2: Trộn 5 run của f1 và f3 vào f2 ÔN TẬP
• Phase 3: Trộn 3 run của f2 và f3 vào f1 XỬ LÝ TẬP TIN VỚI C++
• Phase 4: Trộn 2 run của f1 và f2 vào f3
• Phase 5: Trộn 1 run của f1 và f3 vào f2
• Phase 6: Trộn 1 run của f2 và f3 vào f1

CTDL2 – Lương Trần Hy Hiến 39


Cấu trúc Nội dung

1. Giới thiệu về tập tin


2. ofstream
– output file stream
– Lớp làm nhiệu vụ ghi dữ liệu ra file
3. ifstream
– input file stream
– Lớp làm nhiệm vụ đọc dữ liệu từ file
4. Một số thao tác khác với tập tin
5. fstream
– file stream
– Lớp làm cả nhiệm vụ đọc và ghi tập tin

CTDL2 – Lương Trần Hy Hiến 41 CTDL2 – Lương Trần Hy Hiến 42

1. C++ File I/O 1. Giới thiệu về tập tin

• Binary file được dùng để lưu trữ dữ liệu


dạng cấu trúc có kích thước cố định
– Khi ghi biến cấu trúc lên file sẽ ghi 1 khối có
kích thước cố định,
– Khi đọc từ file ra biến cấu trúc cũng đọc từ file
theo từng khối có kích thước cố định.
• Muốn lập trình với file cần phải biết dạng
lưu trữ cũng như ý nghĩa của dữ liệu trong
file.

CTDL2 – Lương Trần Hy Hiến 43 CTDL2 – Lương Trần Hy Hiến 44


1. Giới thiệu về tập tin 2. ofstream
• Khai báo và mở tập tin để ghi dữ liệu
• Input file : file có dữ liệu sẽ được – ofstream <tên biến>;
truy cập để đưa vào biến – ofstream <tên biến>(<tên file>);
– Ví dụ: ofstream fout;
fout.open(“data.dat”);
hoặc ofstream fout(“data.dat”);

• 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;

CTDL2 – Lương Trần Hy Hiến 45 CTDL2 – Lương Trần Hy Hiến 46

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;

CTDL2 – Lương Trần Hy Hiến 49 CTDL2 – Lương Trần Hy Hiến 50

3. ifstream 4. Một số thao tác khác với tập tin


• Ví dụ • Lấy vị trí hiện tại của con trỏ tập tin
Đọc dữ liệu của 5 phần tử số nguyên liên – Khi đọc
tiếp đã ghi dạng nhị phân • Dùng hàm tellg();
----------------------------------------------- • Ví dụ: long pos=fin.tellg();
int a[10]; – Khi ghi
ifstream fin(“data.dat”); • Dùng hàm tellp();
• Ví dụ: long pos=fout.tellp();
int n=5;
fin.read((char*)a,n*sizeof(*a));
fin.close();
CTDL2 – Lương Trần Hy Hiến 51 CTDL2 – Lương Trần Hy Hiến 52
4. Một số thao tác khác với tập tin 4. Một số thao tác khác với tập tin
• Di chuyển con trỏ đến một vị trí trong tập
tin long SizeOf(char *fileName)
{
– Khi đọc
long end,begin;
• Dùng hàm seekg(long sobyte, ios::xx vtbd);
• Ví dụ: fin.seekg(0,ios::beg); //về đầu tập tin ifstream myfile(fileName);
– Khi ghi begin = myfile.tellg();
• Dùng hàm seekp(long sobyte, ios::xx vtbd); myfile.seekg(0, ios::end); Tính kích thước
• Ví dụ: fout.seekp(0,ios::end); //đến cuối tập tin end = myfile.tellg(); của tập tin
– Hằng số myfile.close();
• ios::beg: di chuyển từ đầu tập tin return end-begin;
• ios::cur: di chuyển từ vị trí hiện tại của con trỏ }
• ios::end: di chuyển từ cuối tập tin

CTDL2 – Lương Trần Hy Hiến 53 CTDL2 – Lương Trần Hy Hiến 54

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, …

CTDL2 – Lương Trần Hy Hiến 55


56
Khai báo mở tập tin để đọc/ghi dữ liệu Một số kiểu truy cập
– Khai báo biến con trỏ tệp
Kiểu truy cập Ý nghĩa
FILE* <tên file>;
“r” Mở một tập tin đã có để đọc theo kiểu VB
– Mở tệp
“w” Mở một tập tin mới để ghi theo kiểu VB
File * fopen(const char *<tên file>, const char
* <kiểu truy cập>); “a” Mở một tập tin mới để ghi bổ sung theo kiểu VB

– 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.

CTDL2 – Lương Trần Hy Hiến 57 CTDL2 – Lương Trần Hy Hiến 58

Một số hàm khác Các hàm nhập xuất ký tự


• Đóng file • Ghi ký tự lên file
int fclose(FILE * f); int fputc(int ch, FILE *f)
• Đóng các file đang mở
Hàm ghi lên file f một ký tự có mã bằng
int fcloseall(void);
m = ch % 256
• Làm sạch vùng đệm
int fflush(FILE* f); • Đọc ký tự từ file
• Kiểm tra cuối file int fgetc(int ch, FILE *f)
int feof(FILE *f) //trả về giá trị khác 0 nếu
gặp cuối tệp, ngược lại trả về 0.

CTDL2 – Lương Trần Hy Hiến 59 CTDL2 – Lương Trần Hy Hiến 60


Các hàm nhập xuất theo kiểu văn bản Ví dụ - hàm fprintf
• Ghi dữ liệu theo khuôn dạng lên file
Chương trình tạo ra tệp văn bản text gồm 3 dòng với
int fprintf(FILE* f, const char * dk,… )
nội dung:
• Đọc dữ liệu từ file theo khuôn dạng
int fscanf(FILE* f, const char * dk,… )
Dong 1
• Ghi một chuỗi ký tự lên file Dong 2
int fputs(const char* s, FILE *f) Dong 3
• Đọc một dãy ký tự từ file -----------------------------------------------
char fgets(const char* s, int n, FILE *f) FILE *f;
Việc đọc kết thúc khi: f =fopen(“D:\\text.txt”,”w”);
- Hoặc đã đọc n-1 ký tự
for(int i=0; i<2; i++)
- Hoặc gặp dấu xuống dòng
- Hoặc kết thúc tệp. fprintf(f,”Dong%2d\n”,i);
fclose(f);
CTDL2 – Lương Trần Hy Hiến 61 CTDL2 – Lương Trần Hy Hiến 62

Ví dụ - hàm fscanf Ví dụ - hàm fputs


Giả sử có một dãy số nguyên ghi trên tệp array.sl, giữa 2 số Chương trình nhập một dòng ký tự từ bàn phím và
có ít nhất 1 khoảng trống. ghi lên tệp “dongVB”
-----------------------------------------------
----------------------------------------------char s[256];

FILE *f;
int c;
while(!feof(f)) f = fopen(“D:\\dongVB.txt”,”w”);
{ printf(“Nhap dong van ban: “);
fscanf(f, “%d”, &c); gets(s);
printf(“%3d”,c); fputs(d,f);
}
fclose(f);

CTDL2 – Lương Trần Hy Hiến 63 CTDL2 – Lương Trần Hy Hiến 64


Ví dụ - hàm fgets Các hàm nhập xuất theo kiểu nhị phân
Chương trình đọc các dòng ký tự trên tệp “dongVB” • Ghi một số nguyên (2byte) lên file
và in ra màn hình int putw(int n, FILE *f)
---------------------------------------------- • Đọc một số nguyên (2byte) lên file
… int getw(FILE *f)
int i=0; char d[256]; • Ghi một số mẫu tin lên file
while(!feof()) int fwrite(void *ptr, int size, int n, FILE *f)
{
• Đọc một số mẫu tin từ file
i++;
fgets(d, 256, f);
int fread(void *ptr, int size, int n, FILE *f)
printf(“Dong %d: %s”,i,d); ptr: con trỏ trỏ tới vùng nhớ chứa dữ liệu đọc được
size: kích thước mẫu tin theo byte
} n: số mẫu tin cần đọc
… f: con trỏ tệp
CTDL2 – Lương Trần Hy Hiến 65 CTDL2 – Lương Trần Hy Hiến 66

Câu hỏi và thảo luận

CTDL2 – Lương Trần Hy Hiến 67


Đại Học Sư Phạm Tp. Hồ Chí Minh
Đề cương môn học: CTDL2
• Chương 1: Sắp xếp ngoại
• Chương 2: Bảng băm (Hash Table)
CẤU TRÚC DỮ LIỆU 2 • Chương 3: B – Cây (B-Tree)
• Chương 4: Cây Đỏ Đen (Red-Black Tree)

Chương 02: BẢNG BĂM

CTDL2 – Lương Trần Hy Hiến 2

Nội dung ĐẶT VẤN ĐỀ


• Giới thiệu bài toán • Cho S là tập hợp n phần tử trong 1 cấu trúc
• Hàm băm dữ liệu được đặc trưng bởi 1 giá trị khóa
• Các phương pháp xử lý đụng độ • Tìm 1 phần tử có hay không trong S
• Phân tích phép băm – Tìm tuyến tính (O(n)), chưa được sắp xếp
– Tìm nhị phân (O(log2n)), đã được sắp xếp
• Có hay chăng 1 thuật toán tìm kiếm với O(1)
– Có, song ta phải tổ chức lại dữ liệu
– Dữ liệu được tổ chức lại là Bảng băm

CTDL2 – Lương Trần Hy Hiến 3 CTDL2 – Lương Trần Hy Hiến 4


Giới thiệu về Bảng Băm Hàm băm
• Là CTDL trong đó các phần tử của nó được lưu trữ • Hàm băm: biến đổi khóa thành chỉ mục trên
sao cho việc tìm kiếm sẽ được thực hiện bằng cách bảng băm
truy xuất trực tiếp thông qua từ khóa. – Khóa có thể là dạng số hay dạng chuỗi
• Bảng băm có M vị trí được đánh chỉ mục từ 0 đến – Chỉ mục được tính từ 0..M-1, với M là số chỉ mục
M-1, M là kích thước của bảng băm. của bảng băm
• Các phương pháp băm: – Hàm băm thường dùng: key % M, M với M là độ lớn
– PP kết nối trực tiếp của bảng băm
– PP kết nối hợp nhất • Hàm băm tốt phải thoả yêu cầu
– PP dò tuyến tính
– Giảm thiểu xung đột
– PP dò bậc 2
– Phân bố đều trên M địa chỉ khác nhau của bảng
– PP băm kép
băm

CTDL2 – Lương Trần Hy Hiến 5 CTDL2 – Lương Trần Hy Hiến 6

Mô tả dữ liệu Ưu điểm bảng băm


• K: tập các khoá (set of keys) • Dung hòa tốt giữa thời gian truy xuất và
• M: tập các địa chỉ (set of addresses). dung lượng bộ nhớ
• HF(k): hàm băm dùng để ánh xạ một khoá k từ tập – Nếu ko giới hạn bộ nhớ: one-to-one, truy xuất
các khoá K thành một địa chỉ tương ứng trong tập tức thì
M. Thông thường HF(k) = k mod M – Nếu dung lượng bộ nhớ có giới hạn thì tổ chức
khóa cùng địa chỉ
• Bảng băm ứng dụng nhiều trong thực tế,
thích hợp tổ chức dữ liệu có kích thước lớn
và lưu trữ ngoài.

CTDL2 – Lương Trần Hy Hiến 7 CTDL2 – Lương Trần Hy Hiến 8


CÁCH XÂY DỰNG BẢNG BĂM Ví dụ một bảng băm đơn giản
– Dùng hàm băm để ánh xạ khóa K vào 1 vị
trí trong bảng băm. Vị trí này như là 1 địa Khóa k sẽ được lưu trữ tại vị trí k mod M (M
chỉ khi tìm kiếm. kích thước mảng)

– Bảng băm thường là mảng, danh sách liên 0 1 2 3 4 5 6 7 8 9


kết, file(danh sách đặc) 95

Thêm phần tử x = 95 vào mảng


95 mod 10 = 5

CTDL2 – Lương Trần Hy Hiến 9 CTDL2 – Lương Trần Hy Hiến 10

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)

CTDL2 – Lương Trần Hy Hiến 11 CTDL2 – Lương Trần Hy Hiến 12


Các phép toán trên hàm băm Vấn đề nảy sinh
• Khởi tạo (Initialize)
• Kiểm tra rỗng (Empty) Giả sử thêm 55 vào mảng
• Lấy kích thước bảng băm (size) 0 1 2 3 4 5 6 7 8 9
• Thêm 1 phần tử vào bảng băm (Insert) 82 95 27
• Xóa 1 phần tử khỏi bảng băm (Remove)
• Duyệt (Traverse) + 55 phải lưu vào vị trí 5. Tuy nhiên vị trí này đã
có chứa 95
k1 ≠ k2 mà f(k1) = f(k2)  Đụng độ
=> Cần giải quyết đụng độ (xung đột)
CTDL2 – Lương Trần Hy Hiến 13 CTDL2 – Lương Trần Hy Hiến 14

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)

CTDL2 – Lương Trần Hy Hiến 15 CTDL2 – Lương Trần Hy Hiến 16


b. Giải quyết xung đột i. Sử dụng DS liên kết (kết nối trực tiếp)

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

*Phân tích Cài đặt - Coding

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

CTDL2 – Lương Trần Hy Hiến 19 16 CTDL2 – Lương Trần Hy Hiến 20


CÀI ĐẶT Khai báo CTDL
• Khai báo cấu trúc dữ liệu
• Khởi tạo bảng băm const int M = 101; //Kích thước bảng băm
• Tạo 1 nút để thêm vào bảng băm struct node
• Xác định hàm băm {
int key;
• Thêm 1 nút vào bảng băm
node* next;
• Xóa 1 phần tử trong bảng băm };
• Tìm 1 phần tử trong bảng băm. typedef node* LINKLIST;
LINKLIST HashTable[M]; //bảng băm

CTDL2 – Lương Trần Hy Hiến 21 CTDL2 – Lương Trần Hy Hiến 22

Khởi tạo bảng băm Tạo 1 nút để thêm


void Init_HashTable() //tạo nút có trường dữ liệu là x, hàm trả về địa
{ chỉ của node mới tạo.
for (int i = 0 ; i< M; ++ i) node* GetNode(int x)
HashTable[i] = NULL; {
}
node *pnew = new node;
//Xác định hàm băm
pnew->key = x;
int Hash(int key)
{ pnew->next = NULL;
return (key % M); return pnew;
} }
CTDL2 – Lương Trần Hy Hiến 23 CTDL2 – Lương Trần Hy Hiến 24
Kiểm tra bảng băm rỗng? Hàm xuất bảng băm
void PrintHT()
bool Empty() {
{ node *p;
for(int i=0;i<M;i++)
for(int b = 0; b < M; b++) {
if(HashTable[b] != NULL) cout<<"Bucket "<<i<<": ";
p = HashTable[i];
return false; while(p!=NULL)
return true; {
cout<<“  “<<p->key;
} p = p->next;
}
cout<<endl;
}
CTDL2 – Lương Trần Hy Hiến 25 } CTDL2 – Lương Trần Hy Hiến 26

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
}

Tìm một phần tử Hàm tạo menu chương trình


// tim 1 phan tu co khoa =key trong bang bam void menu()
node* Search(int x) {
{ int i = Hash(x); puts("DEMO HASHTABLE dung DSLK");
node* p = HashTable[i];
if(p != NULL) puts("1. Them 1 phan tu vao bang bam");
{ puts("2. Xoa 1 phan tu trong bang bam");
while((p != NULL)&&(p->key != x)) puts("3. Tim 1 phan tu co khoa x");
p = p->next; puts("4. Xuat Hash Table");
}
return p; puts("0. Ket thuc");
} }

CTDL2 – Lương Trần Hy Hiến 31 CTDL2 – Lương Trần Hy Hiến 32


Chương trình chính Chương trình chính
int main() switch(chon)
{
{ case 1:
int chon, x; do
{
Init_HashTalbe();//khoi tao bang bam cout<<"Nhap khoa can them: "; cin>>x;
do if(x > 0) Insert(x);
}while(x > 0);
{ menu(); break;
cout<<"Chon chuc nang: "; case 2:
cout<<"Nhap khoa can xoa (key > 0): ";
cin>>chon; cin>>x;
cout<<endl; if(x > 0) Delete(x);
break;
CTDL2 – Lương Trần Hy Hiến 33 CTDL2 – Lương Trần Hy Hiến 34

Chương trình chính ii. Sử dụng PP “kết nối hợp nhất”


case 3:
cout<<"Nhap khoa can tim (key > 0): "; -Ý tưởng: “Nếu có 1 khóa bị băm vào vị trí đã có
cin>>x;
if(x > 0){
phần tử thì nó sẽ được chèn vào ô trống phía
if(Search(x) != NULL) cuối mảng”. (Dùng mảng có M phần tử)
cout<<"Tim thay “<<x;
else 0
cout<<"Tim khong thay"; Hàm băm:
}
1 21
Thêm F(k) = k mod 5
break; 2 16
case 4: PrintHT(); break; 6, 16
} 3 18
} 4 6
while(chon != 0);
return 0;
CTDL2 – Lương Trần Hy Hiến 35 CTDL2 – Lương Trần Hy Hiến 36
}
iii. Sử dụng PP “Dò tuyến tính” *Phân tích
-Ý tưởng: “Nếu có 1 khóa bị băm vào vị trí đã có
phần tử thì nó sẽ được chèn vào ô trống gần - PP này dễ thực hiện
nhất” theo phía bên phải (hoặc trái) - Nếu có nhiều phần tử băm trùng nhau thì
đặc tính bảng băm bị mất đi
0 1 2 3 4
Hàm băm: - Trong trường hợp xấu nhất tìm kiếm trên
21 6 18 16 F(k) = k mod 5 bảng băm thành tìm kiếm tuyến tính trên mảng
Thêm
6, 16
f(key)=(f(key)+i) % M với f(key) là hàm băm chính của bảng băm.

CTDL2 – Lương Trần Hy Hiến 37 CTDL2 – Lương Trần Hy Hiến 38

Cài đặt code

-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

CTDL2 – Lương Trần Hy Hiến 39 CTDL2 – Lương Trần Hy Hiến 40


Khởi tạo bảng băm Kiểm tra bảng băng rỗng?
bool Empty()
void Init_HashTalbe() {
{ for(int b = 0; b < M; b++)
for (int i = 0 ; i< M; ++ i)
if(HashTable[i] != -1)
HashTalbe[i] = -1 ;
} return false;
return true;
}

CTDL2 – Lương Trần Hy Hiến 41 CTDL2 – Lương Trần Hy Hiến 42

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 – Lương Trần Hy Hiến 45 CTDL2 – Lương Trần Hy Hiến 46

Cài đặt – Băm kép (Double Hashing) code

const int M = 101; //Kich thuoc bảng băm

-Dò tuyến tính: pos = (pos + 1) % M int HashTable[M]; //bảng băm


- Băm kép: pos = (pos + u) % M
u = Hash2 (key)

CTDL2 – Lương Trần Hy Hiến 47 CTDL2 – Lương Trần Hy Hiến 48


Khởi tạo bảng băm 2 Hàm băm

int Hash(int key)


void Init_HashTalbe() {
{ return key % M
for (int i = 0 ; i< M; ++ i) }
HashTalbe[i] = -1 ;
} int Hash2(int key)
{
return (M-2) – key % (M-2);
}

CTDL2 – Lương Trần Hy Hiến 49 CTDL2 – Lương Trần Hy Hiến 50

Thêm một khoá vào bảng băm Bài tập


void Insert(int k) Viết chương trình hiện thực từ điển Anh - Việt. Mỗi nút của
{ //Xác định vị trí của khoá k bảng băm có khai báo các trường sau:
int pos = Hash(key) – Trường word là khoá chứa một từ tiếng anh.
– Trường mean là nghĩa tiếng Việt.
//Kiểm tra đảm bảo vị trí pos là trống
– Trường next là con trỏ chỉ nút kế nếu bị xung đột.
while (HashTable[pos] != -1 ){ Tập khoá là một chuỗi tiếng anh, tập địa chỉ có 26 chữ cái.
int u = Hash2 (key); Chọn hàm băm sau cho khoá bắt đầu bằng ký tự a được băm
pos = (pos + u ) % M; vào địa chỉ 0, b băm vào địa chỉ 1,…, z băm vào địa chỉ 25.
Chương trình có những chức năng như sau:
//Kiểm tra full
1. Nhập vào một từ
} 2. Xem từ điển theo ký tự đầu.
HashTable[pos] = key; 3. Xem toàn bộ từ điển.
} 4. Tra từ điển.
5. Xoá một từ, xóa toàn bộ từ điển.
CTDL2 – Lương Trần Hy Hiến 51 CTDL2 – Lương Trần Hy Hiến 52
Đại Học Sư Phạm Tp. Hồ Chí Minh
Đề cương môn học: CTDL2
• Chương 1: Sắp xếp ngoại
• Chương 2: Bảng băm (Hash Table)
CẤU TRÚC DỮ LIỆU 2 • Chương 3: B – Cây (B-Tree)
• Chương 4: Cây Đỏ Đen (Red-Black Tree)

Chương 03: B-Cây

CTDL2 – HIENLTH
HIENLTH, HCMUP 2

Nội dung Đặt vấn đề


• Đặt vấn đề
• Cần lưu trữ số phần tử dữ liệu rất lớn
• B-Tree
• Các phép biến đổi
• Lưu trữ trên bộ nhớ ngoài
• Ứng dụng của B-Tree
• Tìm kiếm nhanh

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

B-Tree Ưu điểm B-Tree


• Định nghĩa: • B-cây là dạng cây cân bằng, phù hợp
B-Tree bậc n có các t/c: với việc lưu trữ trên đĩa
– Mỗi nút có tối đa 2*n khóa
– Mỗi nút (không là gốc) có ít nhất n nút • B-cây tiêu tốn số phép truy xuất đĩa tối
– Mỗi nút hoặc là lá hoặc có m+1 nút con thiểu cho các thao tác
– Các khóa được sắp tăng dần từ trái sang phải
– Các nút lá nằm cùng một mức • Có thể quản lý số phần tử rất lớn

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

Cây B (n = 1) – Chèn Ví dụ thao tác thêm (B-Tree: n = 1)


• Chèn Thêm các nút: 1,2,3,4,5,6,7

– 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

Ví dụ thao tác thêm (B-Tree: n = 2) Ví dụ thao tác thêm (B-Tree: n = 2)


Thêm các khóa 6, 14, 28
8 Thêm các khóa 7, 52, 16, 8 17

vào các nút lá: 48 vào các nút lá


1 2 6 12 14 25 28 1 2 6 7 12 14 16 25 28 48 52

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

• Xóa 48 • Xóa 44 (gộp trang)


12 24 37
12 24

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

• Xóa 24, ta đem khóa 22 lên thay thế. Khi đó


17 32

trang 20,22 chỉ còn 20 (phạm), ta phải đem


khóa 22 trở lại trang 20,22. Mang 28 lên
thêm 17 28 9 12 22 28 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

Trường hợp: VD 2 – Xoá 1 khoá trong B-Tree cấp 1


Nút đang xét và nút anh em kế cận đề còn • Ví dụ ta có cây
quá ít khóa như sau:
12 29 56

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

Xoá 1 nút trong B - Tree VD 2 – Xoá 1 khoá trong B-Tree (tt)


• Xoá khoá 18, 8 thay thế

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

Cây B+ - Duyệt theo thứ tự giữa Cây (B+) – Sử dụng


• Cây B+ (B+ trees) • Sử dụng - Cơ sở dữ liệu lớn
– Đọc một khối đĩa chậm hơn nhiều so với đọc bộ nhớ ( ~ms vs ~ns )
– Liên kết các lá – Đặt từng khối của các khóa vào trong một khối đĩa
• 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. Physical disc
blocks

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

Câu hỏi và thảo luận

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

a. Khái niệm cây đỏ đen Các tính chất

1. Mọi nút chỉ có thể là đỏ (RED) hoặc đen


(BLACK)
-Là cây nhị phân tìm kiếm 2. Nút gốc mang màu đen. Mọi nút lá (NIL)
-Đưa thêm yếu tố màu (RED, BLACK) để làm mang màu đen.
cơ sở cho sự cân bằng. 3. Nếu 1 nút là đỏ thì con của nó phải là đen
(nghĩa là không có 2 nút đỏ liên tiếp)
4. 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.

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

Nhận xét Cây đỏ đen


• Cây đỏ đen có N nút trong (internal node) có Cấu trúc lưu trữ:
chiều cao tối đa là 2logn(N+1) • Thông tin lưu trữ tại node (key)
• Chiều cao của cây (h) <= 2 * chiều cao đen • Địa chỉ node gốc của cây con bên trái
(hb) (*pLeft)
• Các nút lá là NIL (nút ngoài – external node) • Địa chỉ node gốc của cây con bên phải
luôn là node đen. (*pRight)
• Mỗi cạnh đến 1 node đen được gọi là cạnh • Địa chỉ node cha (*pParent)
đen. • Thuộc tính màu của node (color)
• Thời gian tìm kiếm O(logN)
CTDL2 – HIENLTH
HIENLTH, HCMUP 7 CTDL2 – HIENLTH
HIENLTH, HCMUP 8
b. Biểu diễn cây đỏ - đen c. Một số thao tác đơn giản (tự viết)
struct Node
{ + Tìm kiếm và duyệt cây (giống BST)
int Key; //Dữ liệu + Thêm node mới
Node* pLeft, *pRight; + Xóa node
int color; //RED - BLACK
Node* pParent; //Trỏ đến nút cha
};
+ Khởi tạo
typedef Node* pNode; void Init (RBTree &t);
struct RBTree{ + Kiểm tra rỗng
pNode root; int isEmpty (RBTree t);
};
CTDL2 – HIENLTH
HIENLTH, HCMUP 9 CTDL2 – HIENLTH
HIENLTH, HCMUP 10

d. Xuất cây ra màn hình d. Xuất cây ra màn hình (tt)


void Print (pNode p, int level)
void PrintTree (RBTree t)
{
{ if (!p)
if (isEmpty(t)) {
{ textcolor(BLACK);
cout<<“Cây rỗng”; for (int i = 0 ; i < level; ++ i)
return; {
} cout<<“ “;
cout<<“NULL “<<endl;
Print(t.root, 0):
}
}

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

Các tính chất có thể vi phạm khi thêm nút


• Mọi node là đỏ hoặc đen  OK void RBTree_Insertion(RBTree &root, int key)
• Node gốc là đen  Not OK nếu thêm node {
mới là node gốc x = getNode(key,RED, NIL);
• Các node lá (NIL) phải luôn đen  OK BSTree_Insert(root, x);
• Nếu 1 node là đỏ, các node con của nó phải Insertion_Fixup(root, x);
là đen  Not OK nếu cha node thêm màu đỏ }
 2 node đỏ liên tiếp.
• Mọi đường dẫn từ gốc đến lá chứa một số
lượng node đen như nhau  OK
CTDL2 – HIENLTH
HIENLTH, HCMUP 15 CTDL2 – HIENLTH
HIENLTH, HCMUP 16
Gọi: Một số định nghĩa
• X: Con trỏ, trỏ đến nút
vừa chèn vào
• P: Con trỏ, nút cha của g

nút trỏ bởi x X là một node cháu ngoại


• U: Con trỏ, trỏ đến nút p
u U U nếu nó nằm cùng bên
anh em của nút trỏ bởi p node cha P và P cùng bên
node cha G.
• G: Con trỏ, trỏ đến nút Ngược lại, X gọi là node
cha của nút trỏ bởi p x x
cháu nội .

Cháu ngoại Cháu nội

U
CTDL2 – HIENLTH
HIENLTH, HCMUP 17 U CTDL2 – HIENLTH
HIENLTH, HCMUP 18

* Phép quay phải tại y Phép quay (như BST)

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;

//nếu x là nút gốc y->pLeft = x;


if (x->pParent == NULL) x ->pParent = y;
t.root = y; //gốc mới sẽ là y }
else
CTDL2 – HIENLTH
HIENLTH, HCMUP 29 CTDL2 – HIENLTH
HIENLTH, HCMUP 30

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

Quay trái tại ông của x đồng thời đổi 50

màu cha của x và ông của x


50 50 80
25

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

TH4: x là con trái, cha x là phải


hoặc x là con phải, cha x là trái
CODE
void Insert(RBTree &t, int k)
Quay trái (phải) tại cha (không đổi màu) {
50 -Tìm vị trí cần chèn k
-Phát sinh 1 nút x có khoá là k, màu đỏ, và chèn vào vị
trí cần chèn
80 //Bắt đầu cân bằng
25 while (x!=t.root && x->pParent->color==RED)
{
75
100 //cha x là con trá của ông x
if ( x -> p == x->pParent->pParent->pLeft)
105 TH.3 {
//y là “bác” của x
y = x->pParent->pParent->pRight;
110
}
CTDL2 – HIENLTH
HIENLTH, HCMUP 35 CTDL2 – HIENLTH
HIENLTH, HCMUP 36
CODE CODE
else
if (y->color == RED){ //TH1 if (x== x->pParent->pRight){ //TH3
x->pParent->Color = BLACK; x = x->pParent;
y->color = BLACK; LeftRotate(t,x);
x->pParent -> pParent -> color = RED }
x = x->pParent -> pParent; //TH2 luôn xảy ra sau đó
} X->pParent ->color = BLACK;
X->pParent->pParent->color = RED;
RightRotae(t,x->pParent->pParent);
}
CTDL2 – HIENLTH
HIENLTH, HCMUP 37 CTDL2 – HIENLTH
HIENLTH, HCMUP 38

CODE Red-Black Trees


Insertion example
11

else //TH: cha của x là con phải của “ông”x 2 14

{
//Làm tương tự như trên,đổi pRight<->pLeft 1 7 15

}
t.root -> color = BLACK; 5 8

4 Violation of rule( after 4 added to the tree)

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

Gọi: Dấu hiệu đen (black token)


• y: Con trỏ, trỏ đến nút bị xóa • Được gán cho nút trỏ bởi x (nút con thật sự của
thật sự nút bị xóa y)
• x: Con trỏ, trỏ đến nút con p • Báo hiệu con đường đi qua các nút có chứa nó sẽ ít
của nút trỏ bởi y, sẽ thay thế hơn những con đường khác 1 nút đen
nút y w
• “dấu hiệu đen” đi ngược lên cây cho tới khi chiều
• w: Con trỏ, trỏ đến nút anh y cao cây được đảm bảo.
em của nút trỏ bởi x • Nút chứa dấu hiệu đen, bản thân là:
• p: Con trỏ, trỏ đến nút cha x – Nút đen  nút đen kép
của nút trỏ bởi y – Nút đỏ  nút đỏ đen
• Là khái niệm trừu tượng, không cài đặt

CTDL2 – HIENLTH
HIENLTH, HCMUP 47 CTDL2 – HIENLTH
HIENLTH, HCMUP 48
Quy ước Quy ước

Do khi xoá và cân bằng cây, sẽ có nhiều pNode Null;


trường hợp cần phải xử lý, trong đó có việc void main()
kiểm tra nút là nút NULL? {
Null -> color = BLACK;
Giải pháp : thay tất cả các nút NULL bằng 1 Null ->pParent= Null->pLeft=Null->pRight = NULL;
nút đặc biệt, có tên là Null. Đây là nút thật sự Null -> Key = 0;
luôn có màu đen, các trị của nó (key, trái, }
phải, cha) được gán bất kỳ

CTDL2 – HIENLTH
HIENLTH, HCMUP 49 CTDL2 – HIENLTH
HIENLTH, HCMUP 50

Phân tích Phân tích


Khi huỷ một nút đen thì chiều cao đen của
nhánh chứa nó sẽ bị sụt giảm 1 đơn vị, ta
Khi nào cần cân bằng lại cây? có thể:
+ Khi nút bị xoá là nút có màu đen + Biến nút đen cùng cấp ở cây con còn lại
thành nút đỏ (nếu được)
Khi cân bằng cần để ý đến sự vi phạm t/c nào?
+ t/c chiều cao cây đen + Thực hiện xoay cây để tạo sự cân bằng

=> KL: Kết hợp đổi màu và xoay cây

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

TH1: x đỏ hoặc gốc TH2: x đen kép, w và 2 con w đều đen


• Nút x chứa “dấu hiệu đen” là nút đỏ • Đổi màu nút anh em w sang đỏ
(nút đỏ - đen) hay nút gốc. • Đổi màu P đen (bất kể P trước đó màu đỏ
Xử lý: hay màu đen)
• Tô màu nút đỏ đen sang màu đen. • Di chuyển “dấu hiệu đen” lên trên 1 cấp
• Loại bỏ dấu hiệu đen và kết thúc (theo hướng gốc của cây)

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

TH3: w đỏ TH4: x đen kép, w đen, 1 trong 2 con w đỏ


Gọi z là nút con của w có màu đỏ.
D
(w có ít nhất một con đỏ gọi là z)
B Có 2 trường hợp xảy ra:
w Left Rotate
E
• 4a) z là cháu nội
x B
• 4b) z là cháu ngoại
D Đổi màu
A
ε η Xử lý:
x
α β C E
A w
C
• 4a) chuyển sang 4b) nhờ phép quay và đổi
mới màu
δ γ ε η α β δ γ • 4b) được xử lý bằng phép quay và đổi màu

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

TH4b: w đen con phải w màu đỏ Bài tập áp dụng


Cho cây đỏ đen như sau:
Đỏ/đen 50
D
p B
Quay trái tại cha của x
25 60
x w D Đổi màu B E
10 30
A

ε η 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

Các loại nút Các trường hợp của cây 2-3-4

2 – node 3 – node 4 – node


Một mục dữ liệu Hai mục dữ liệu Ba mục dữ liệu
2 node con. 3 node con. 4 node con.

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

Cấu trúc một nút Tổ chức cây 2-3-4


• Trong cây 2-3-4, các nút lá đều nằm trên
cùng một mức. Các node ở mức trên thường
struct Node không đầy đủ, nghĩa là chúng có thể chứa
{ chỉ 1 hoặc 2 mục dữ liệu thay vì 3 mục.
char key[3]; • Lưu ý rằng cây 2-3-4 là cây cân bằng. Nó
int type; // 2 , 3, 4 – Node vẫn giữ được sự cân bằng khi thêm vào các
Node* next[4]; phần tử có thứ tự (tăng dần hoặc giảm dần).
};

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

Tách nút gốc Ví dụ về tách node gốc


• Node gốc đầy  tách trước, chèn sau
• Giả sử node gốc có 3 mục dữ liệu A, B, C (đã được
sắp thứ tự). Quá trình tách:
– Node mới được tạo ra để trở thành gốc mới và là cha của
node được tách.
– Node mới thứ hai được tạo ra để trở thành anh em với
node được tách.
– Mục dữ liệu C được dịch đưa sang node anh em mới.
– Mục dữ liệu B được dịch đưa sang node gốc mới.
– Mục dữ liệu A vẫn không đổi.
– Hai node con bên phải nhất của node được phân chia bị
hủy kết nối khỏi nó và kết nối đến node mới bên phải.

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

Ví dụ về chèn nút Ví dụ về xóa node


• Khóa k nằm trong nút lá u và u có nhiều hơn
một khóa: giải phóng khóa k khỏi u .

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

Ví dụ về xóa node Đánh giá


• Khóa k nằm trong nút trong u: Khi đó tìm khóa tiền nhiệm
hoặc khóa kế vị của k (khóa này luôn nằm trong nút lá).
Thay k bởi khóa đó, và giải phóng khóa đó tkhỏi nút chứa
nó (quay về trường hợp 1). Tuy việc dùng khóa tiền nhiệm
hay kế vị đều được, nhưng nên chọn khóa nào trong chúng + Cây cân bằng
nằm trong nút có hai khóa trở lên, nếu cả hai đều nằm
trong các 2-nút thì chọn khóa nào cũng được. + Chi phí tìm kiếm là O(log(n))

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

Node cha: đen


Node con: đỏ
CTDL2 – HIENLTH
HIENLTH, HCMUP 89 CTDL2 – HIENLTH
HIENLTH, HCMUP 90

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 đỏ.

Node cha: đen


Node con: đỏ
CTDL2 – HIENLTH
HIENLTH, HCMUP 91 CTDL2 – HIENLTH
HIENLTH, HCMUP 92

You might also like