You are on page 1of 188

TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI

VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG

Cấu trúc dữ liệu và thuật toán

Nguyễn Khánh Phương

Computer Science department


School of Information and Communication technology
E-mail: phuongnk@soict.hust.edu.vn
Nội dung khóa học
Chương 1. Các khái niệm cơ bản
Chương 2. Các sơ đồ thuật toán
Chương 3. Các cấu trúc dữ liệu cơ bản
Chương 4. Cây
Chương 5. Sắp xếp
Chương 6. Tìm kiếm
Chương 7. Đồ thị
2
TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG

Chương 4. Cây

Nguyễn Khánh Phương

Computer Science department


School of Information and Communication technology
E-mail: phuongnk@soict.hust.edu.vn
Nội dung
1. Định nghĩa và các khái niệm
2. Biểu diễn cây
3. Duyệt cây
4. Cây nhị phân

Become Rich

Force Others Rob Stock


to be Poor Banks Fraud 4
Nội dung
1. Định nghĩa và các khái niệm
2. Biểu diễn cây
3. Duyệt cây
4. Cây nhị phân

Become Rich

Force Others Rob Stock


to be Poor Banks Fraud 5
1. Định nghĩa và các khái niệm
1.1. Định nghĩa
1.2. Các thuật ngữ
1.3. Cây có thứ tự (Ordered tree)

6
1. Định nghĩa và các khái niệm
1.1. Định nghĩa
1.2. Các thuật ngữ
1.3. Cây có thứ tự (Ordered tree)

7
1.1. Định nghĩa
Cây gồm một tập các nút, có 1 nút đặc biệt gọi là gốc (root) và các
cạnh nối các nút.
Nút gốc
Dusty
nút
Honey B ear B randy

B runhilde T erry C oyote Nugget

Gill T ansey T weed Zoe C rocus Primrose Nous B elle

nút

8
Cây: Định nghĩa đệ quy
• Bước cơ sở:
– Cây rỗng: cây không có nút nào
– Cây có 1 nút r : nút r được gọi là gốc của cây
• Bước đệ quy:
– Giả sử T1,T2,...,Tk là các cây với gốc tương ứng là r1,r2,...,rk
– Ta có thể xây dựng cây mới bằng cách đặt nút r làm cha (parent) của các nút
r1,r2,....,rk :
• Trong cây mới này: r là gốc; T1, T2, . . . , Tk là các cây con của gốc r; Các nút
r1,r2,....,rk được gọi là con (children) của nút r.
r Gốc (Root)

r1 r2 rk

T1 T2 Tk 9
Các ứng dụng của cây
• Lịch thi đấu
• Cây gia phả
• Cây thư mục
• Cấu trúc của 1 quyển sách
• Cây biểu thức
• Sơ đồ phân cấp của 1 cơ quan
• ….
Các ứng dụng của cây: Biểu đồ lịch thi đấu
• Trong đời thường, cây thường được sử dụng để mô tả lịch
thi đấu của các giải thể thao theo thể thức đầu loại trực tếp

France France
Spain France
Brazil Brazil
UK Italia
Germany Germany
Ukraine
Italia Italia
Italia
Argentina
Các ứng dụng của cây: Cây gia phả
• Cây gia phả của các nhà toán học dòng họ Bernoulli

Nikolaus
1623-1708

Johan I Nikolaus Jacob I


1667-1748 1662-1716 1654-1705

Nikolaus II Daniel Johan II Nikolaus I


1695-1726 1700-1782 1710-1790 1687-1759

Johan III Jacob II


1746-1807 1759-1789
Các ứng dụng của cây: Cây thư mục
• Cây thư mục
Các ứng dụng của cây: Mục lục 1 quyển sách
Các ứng dụng của cây: Cây gia phả ngược (Ancestor Tree)

• Mỗi người đều có bố mẹ:


Ví dụ: Chiến có cha mẹ là: Thanh và Mai

Chiến

Thanh Mai

Giang Minh Thành Lan Anh


Các ứng dụng của cây: Cây biểu thức

/ /

1 3 * 4

6 7

1/3 + 6*7 / 4
Các ứng dụng của cây: Sơ đồ tổ chức của cơ quan

Công ty máy tính MU

Bộ phận bán hàng Bộ phận sản xuất Nghiên cứu và phát triên (R&D)

Mỹ Quốc tế Laptops Desktops

Châu Âu Châu Á Canada


1. Định nghĩa và các khái niệm
1.1. Định nghĩa
1.2. Các thuật ngữ
1.3. Cây có thứ tự (Ordered tree)

18
1.2. Các thuật ngữ
• Nút (node)
• Gốc (Root)
• Lá (leaf)
• Con (child)
• Cha (Parent)
• Tổ tiên (Ancestors)
• Hậu duệ (Descendants)
• Anh em (Sibling)
• Nút trong (Internal node)
• Chiều cao (Height)
• Chiều sâu (Depth)
1.2. Các thuật ngữ
• Gốc: nút không có cha (ví dụ: nút A)
• Anh em: nút có cùng cha (ví dụ: B, C, D là anh em; I, J, K là anh em; E và F là anh
em; G và H là anh em)
• Nút trong: nút có ít nhất 1 con (ví dụ: các nút màu xanh dương: A, B, C, F)
• Nút ngoài (lá ): nút không có con (ví dụ: các nút xanh lá: E, I, J, K, G, H, D)
• Tổ tiên của 1 nút: là các nút cha / ông bà / cụ.. của nút đó.
• Hậu duệ của 1 nút: là các nút con/cháu/chắt… của nút đó
• Cây con của 1 nút: là cây có gốc là con của nút đó

Ví dụ: A
Con của B:
• E, F
Cha của E: B C D
• B
Tổ tiên của F:
• B, A E F G H
Hậu duệ của B:
• E, F, I, J, K Cây con của nút A
I J K
1.2. Các thuật ngữ
• Đường đi (Path): là 1 dãy các nút và các cạnh nối 1 nút đến 1 hậu duệ của nó
• Độ dài (Length) của đường đi = số lượng cạnh trên đường đi = số lượng nút trên đường đi - 1
(Ví dụ: Độ dài đường đi (A  C E) = 2; độ dài đường đi (CF) = 1)
Như vậy, đường đi độ dài 0 là đường đi từ 1 nút đến chính nó
• Độ sâu/mức (Depth/level) của 1 nút N = 1 +độ dài đường đi từ gốc đến nút N
• Chiều cao (Height) của nút N = 1 + độ dài đường đi dài nhất từ N đến lá
• Chiều cao (sâu) của cây = chiều cao của gốc (= chiều cao lớn nhất của tất cả các nút trên cây)

sâu=1 A cao=3
Chiều cao của cây= 3

sâu=2 B sâu=2 C sâu=2 D


cao=1 cao=2
cao=1

sâu=3 E sâu=3 F
cao=1 cao=1
Chiều cao và độ sâu/mức

Sâu 1
Cao c = 5 7

c=4 3 10 4 sâu 2

c=3 8 12 11 2 sâu 3

c=2 6 5 1 sâu 4

9 Chiều cao của cây = 5 sâu 5


Cao c=1
1.2. Các thuật ngữ
• Bậc (Degree) của 1 nút: số lượng nút con của nút đó (= số lượng cây con
của nút đó)
• Bậc (Degree) của 1 cây: bậc lớn nhất của tất cả các nút trên cây

Bậc = 2 A
Bậc của cây = ?

2 B 3 C

0 E F 3 0 G H 0 D 0

I J K
0 0 0
Bài tập: Các thuộc tính của cây

Thuộc tính Giá trị


A
1) Số nút
2) Nút gốc
B C 3) Các nút lá
4) Các nút trong
5) Tổ tiên của H
D E F 6) Con cháu của B
7) Anh em của E
8) Cây con phải của A
9) Cây con trái của A
G
10) Chiều cao của cây
11) Bậc của cây
H I
How we view a tree

root

root
Nature Lovers View Computer Scientists View

25
1. Định nghĩa và các khái niệm
1.1. Định nghĩa
1.2. Các thuật ngữ
1.3. Cây có thứ tự (Ordered tree)

26
1.3. Cây có thứ tự (Ordered tree)
Cây có thứ tự là cây mà các nút được sắp xếp theo thứ tự
Ví dụ: Nếu T1 và T2 là cây có thứ tự thì T1 ≠ T2; nếu không T1 = T2

a a

b c c b
T1 T2

27
Nội dung
1. Định nghĩa và các khái niệm
2. Biểu diễn cây
3. Duyệt cây
4. Cây nhị phân

28
2. Biểu diễn cây
• Một số cách cài đặt cây trên máy tính
– Mảng
– Danh sách các con (Lists of Children)
– Con trái nhất, anh em bên phải (The Leftmost-Child, Right-Sibling)

29
2. Biểu diễn cây: Mảng
• Giả sử cây T có n nút, được đánh số 1, 2, …, n.
• Ta sử dụng 1 mảng A để biểu diễn cây T như sau:
1
– A[i] = j nếu nút j là cha của nút i
– Gốc của cây T không có cha, do đó A[1] = 0
2 3

4 5 6 10

7 8 9

A 0 1 1 2 2 3 6 6 6 3
A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9] A[10]

30
2. Biểu diễn cây
• Một số cách cài đặt cây trên máy tính
– Mảng
– Danh sách các con (Lists of Children)
– Con trái nhất, anh em bên phải (The Leftmost-Child, Right-Sibling)

31
2. Biểu diễn cây: Danh sách các con
1 2 3

1
2 4 5
3 6 10

3 4 
2
5 
6 7 8 9
4 5 6 10 7 
8  • Danh sách các con của nút 6
7 8 9
9  • Ta dùng danh sách liên kết
10  để biểu diễn danh sách này
header
Header : mảng các con trỏ
• Mỗi con trỏ header[i] trỏ đến đầu danh sách chứa các con của nút i trên cây
• Danh sách các con của nút i: được biểu diễn bởi 1 danh sách liên kết

32
2. Biểu diễn cây
• Một số cách cài đặt cây trên máy tính
– Mảng
– Danh sách các con (Lists of Children)
– Con trái nhất, em kế cận bên phải (The Leftmost-Child, Right-
Sibling)

33
2. Biểu diễn cây : Con trái nhất, em kế cận bên phải
• Mỗi nút trên cây, có thể:
– Không có con nào, hoặc có duy nhất 1 con trái nhất
– Không có anh em nào, hoặc có duy nhất 1 anh em bên phải

A
Ví dụ 1: nút B
• Con trái nhất của B: E
• Anh em bên phải của B: C
B C D
Ví dụ 2: nút C
• Không có con
E F G H I • Con trái nhất của C:
• Anh em bên phải của C: D
J K L
Ví dụ 3: nút I
• Không có con
• Không có anh em bên phải 34
2. Biểu diễn cây : Con trái nhất, em kế cận bên phải
• Mỗi nút trên cây, có thể:
– Không có con nào, hoặc có duy nhất 1 con trái nhất
– Không có anh em nào, hoặc có duy nhất 1 anh em bên phải
Do đó, để biểu diễn cây, ta sẽ lưu trữ các thông tin về con trái nhất và anh em
bên phải của mỗi nút:
Data
Con trái nhất Em kế cận phải
typedef struct
{
int data; // dữ liệu có trên mỗi nút
struct treeNode * con_trái_nhất;
struct treeNode * em_kế_cận_phải;
}treeNode;
treeNode * Root;
35
2. Biểu diễn cây : Con trái nhất, em kế cận bên phải

A Data
Con trái nhất Em kế cận phải

B C D

E F G

A
H I J K

B C D

E F G

H I J K
2. Biểu diễn cây : Con trái nhất, em kế cận bên phải

1 Data
Con trái nhất Em kế cận phải

2 1

2
3 5

4 11 7
3 5

6 4
11 7
8
6
-2 9

-2 9
Nội dung
1. Định nghĩa và các khái niệm
2. Biểu diễn cây
3. Duyệt cây
4. Cây nhị phân

38
3. Duyệt cây (Tree Traversal)
Có 3 phương pháp chính:
• Thứ tự trước (Preorder):
– Thăm nút gốc
– Duyệt theo thứ tự trước các nút con (cây con)

• Thứ tự sau (Postorder):


– Duyệt các con (cây con) theo thứ tự sau
– Thăm nút gốc

• Thứ tự giữa (Inorder):


– Duyệt con trái nhất (cây con trái nhất) theo thứ tự sau
– Thăm nút gốc
– Duyệt các con còn lại (không phải con trái nhất) theo thứ tự sau
Duyệt thứ tự trước (Preorder Traversal)
Khi duyệt cây theo thứ tự trước, mỗi nút đều được thăm trước con cháu của nó.
Ví dụ: Duyệt cây T theo thứ tự trước:
procedure preorder(T: ordered rooted tree)
r r := root of T
visit r Step 1
r2
for each child c of r from left to right
r1 rk
begin
T(c) := subtree with c as its root
T1 T2 Tk
preorder(T(c)) Step 2, 3…
end

– Bước 1: thăm nút gốc r,


– Bước 2: thăm cây con T1 theo thứ tự trước,
– Bước 3: thăm cây con T2 theo thứ tự trước
– ……..
– Bước k+1: thăm cây con Tk theo thứ tự trước
Thứ tự trước (PreOrder)
1. Thăm gốc
2. Thăm cây con trái nhất theo thứ tự trước b
3. Lần lượt thăm các cây con còn lại theo thứ tự trước

e f

j k

n o p

b
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo thứ tự trước b
3. Lần lượt thăm các cây con còn lại theo thứ tự trước

e f

j k

n o p

b
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo thứ tự trước
2.1. Thăm gốc b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder
e f

j k

n o p

b
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder
e f

j k

n o p

be
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder
e f

j k

n o p

be j
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder
e f

j k

n o p

be j
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k

n o p

be j k
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k

n o p

be j k n
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k

n o p

be j k n o
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k

n o p

be j k n o p
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k DONE

n o p

be j k n o p
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
e DONE f
2.3.2. Thăm cây con trái nhất theo preorder
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k

n o p

be j k n o p
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc
b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

j k

n o p

be j k n o p f
PreOrder
1. Thăm gốc
2. Thăm cây con trái nhất theo preorder
2.1. Thăm gốc b
2.2. Thăm cây con trái nhất theo preorder
2.3. Lần lượt thăm các cây con theo preorder
2.3.1. Thăm gốc
2.3.2. Thăm cây con trái nhất theo preorder e f
2.3.3. Lần lượt thăm các cây con còn lại theo preorder
3. Lần lượt thăm các cây con còn lại theo preorder

DONE
j k

n o p

be j k n o p f
Duyệt thứ tự trước (Preorder Traversal)
• Ứng dụng: in ra cấu trúc của một quyển sách

procedure preorder(T: ordered rooted tree)


r := root of T
visit r
for each child c of r from left to right
begin
T(c) := subtree with c as its root
preorder(T(c))
end
1
Become Rich

2 5 9
1. Motivations 2. Methods 3. Success Stories

3 4 6 7 8
1.1 Enjoy 1.2 Help 2.1 Get a 2.2 Start a 2.3 Acquired
Life Poor Friends CS PhD Web Site by Google
Duyệt thứ tự sau (Postorder Traversal)
Khi duyệt cây theo thứ tự sau, mỗi nút đều được thăm trước con cháu của nó.
Ví dụ: Duyệt cây T theo thứ tự sau:
r procedure postorder(T: ordered rooted tree)
r := root of T
for each child c of r from left to right
r2 rk
r1 begin
T(c) := subtree with c as its root
T1 T2 Tk postorder(T(c))
end
visit r

– Bước 1: thăm cây con T1 theo thứ tự sau,


– Bước 2: thăm cây con T2 theo thứ tự sau,
– ……..
– Bước k: thăm cây con Tk theo thứ tự sau,
– Bước k+1: thăm nút gốc r
Duyệt thứ tự sau (Postorder Traversal)
Khi duyệt cây theo thứ tự sau, mỗi nút đều được thăm trước con cháu của nó.
Ví dụ: Duyệt cây T theo thứ tự sau:
r procedure postorder(T: ordered rooted tree)
r := root of T
for each child c of r from left to right
r2 rk
r1 begin
T(c) := subtree with c as its root
T1 T2 Tk postorder(T(c))
end
visit r

– Bước 1: thăm cây con T1 theo thứ tự sau,


– Bước 2: thăm cây con T2 theo thứ tự sau,
– ……..
– Bước k: thăm cây con Tk theo thứ tự sau,
– Bước k+1: thăm nút gốc r
PostOrder
1. Thăm cây con trái nhất theo Postorder
2. Thăm lần lượt các cây con còn lại theo Postorder b
3. Thăm gốc

e f

j k

n o p
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc e f

j k

n o p
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc e f

j k

n o p

j
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc e f

j k

n o p

j
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n o
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n o p
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k DONE

n o p

j n o p k
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n o p k e
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
e f
1.2.3. Thăm gốc
1.3. Thăm gốc
Done
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n o p k e
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n o p k e f
PostOrder
1. Thăm cây con trái nhất theo Postorder
1.1. Thăm cây con trái nhất theo Postorder b
1.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.1. Thăm cây con trái nhất theo Postorder
1.2.2. Thăm lần lượt các cây con còn lại theo Postorder
1.2.3. Thăm gốc e f
1.3. Thăm gốc
2. Thăm lần lượt các cây con còn lại theo Postorder
3. Thăm gốc
j k

n o p

j n o p k e f b
Duyệt thứ tự sau (Postorder Traversal)
• Ứng dụng: tính dung lượng bộ nhớ bị chiếm giữ bởi các file trong một thư
mục và các thư mục con của nó
procedure postorder(T: ordered rooted tree)
r := root of T
for each child c of r from left to right
begin
T(c) := subtree with c as its root
postorder(T(c))
end
visit r
9
cs16/

8
3 7
todo.txt
homeworks/ programs/
1K

1 2 4 5 6
h1c.doc h1nc.doc DDR.java Stocks.java Robot.java
3K 2K 10K 25K 20K
Duyệt thứ tự giữa (Inorder Traversal)
• Duyệt cây T theo thứ tự giữa:
r
procedure inorder(T: ordered rooted tree)
r := root of T
r1 r2 rk if r is a leaf then visit r
else
T1 T2 Tk begin
l := first child of r from left to right
T(l) := subtree with l as its root
inorder(T(l)) Step 1
• Bước 1: thăm T1 theo thứ tự giữa, visit r Step 2
• Bước 2: thăm nút gốc r, for each child c of r except for l left to right
• Bước 3: thăm T2 theo thứ tự giữa, T(c) := subtree with c as its root
• …….. inorder(T(c)) Step 3, 4..

• Bước k+1: thăm Tk theo thứ tự giữa end


InOrder
1. Thăm cây con trái nhất theo InOrder
2. Thăm gốc b
3. Lần lượt thăm các cây con còn lại theo Inorder

e f

j k

n o p
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm các cây con còn lại theo Inorder e f

j k

n o p
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder e f

j k

n o p

j
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder e f

j k

n o p

j e
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder e f

j k

n o p

j e
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n k
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n k o
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n k o p
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k DONE

n o p

j e n k o p
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e DONE f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n k o p
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n k o p b
InOrder
1. Thăm cây con trái nhất theo Inorder
1.1. Thăm cây con trái nhất theo Inorder
1.2. Thăm gốc
b
1.3. Thăm lần lượt các cây con còn lại theo Inorder
1.3.1. Thăm cây con trái nhất theo Inorder
1.3.2. Thăm gốc e f
1.3.3. Thăm lần lượt các cây con còn lại theo Inorder
2. Thăm gốc
3. Thăm lần lượt các cây con còn lại theo Inorder
j k

n o p

j e n k o p b f
Ví dụ 1: Duyệt cây
Cho cây có thứ tự T có nút gốc 7. Hãy đưa ra thứ tự các nút được thăm nếu ta
duyệt cây T theo thứ tự:
• Trước (Preorder) 7
• Giữa (Inorder)
• Sau (Postorder)
3 10 4

8 12 11 2

6 5 1

9
Preorder: Root, left child, right child
• 7,
• 3,
• 8, 7
• 6,
• 5,
3 10 4
• 9,
• 12,
• 1, 8 12 11 2
• 10,
• 4,
6 5 1
• 11,
• 2
9
Inorder: Left child, Root, right child
• 6,
• 8,
• 9, 7
• 5,
• 3,
3 10 4
• 12,
• 1,
• 7, 8 12 11 2
• 10,
• 11,
6 5 1
• 4,
• 2
9
Postorder: Left child, right child, Root
• 6,
• 9,
• 5, 7
• 8,
• 1,
3 10 4
• 12,
• 3,
• 10, 8 12 11 2
• 11,
• 2,
6 5 1
• 4,
• 7
9
Ví dụ 2: Duyệt cây
Cho cây có thứ tự T có nút gốc a. Hãy đưa ra thứ tự các nút được thăm nếu ta
duyệt cây T theo thứ tự:
• Trước (Preorder) a
• Giữa (Inorder)
• Sau (Postorder)
b c d

e f g h i

j k l m

n o p
Nội dung
1. Định nghĩa và các khái niệm
2. Biểu diễn cây
3. Duyệt cây
4. Cây nhị phân (Binary tree)

91
4. Cây nhị phân (Binary tree)
4.1. Định nghĩa và tính chất
4.2. Biểu diễn cây nhị phân
4.3. Duyệt cây nhị phân
4.4. Một số ứng dụng

92
4. Cây nhị phân (Binary tree)
4.1. Định nghĩa và tính chất
4.2. Biểu diễn cây nhị phân
4.3. Duyệt cây nhị phân
4.4. Một số ứng dụng

93
4.1. Định nghĩa: Binary tree
• Cây nhị phân là cây mà mỗi nút có nhiều nhất là hai con.
 Mỗi nút trên cây hoặc là: (1) không có con nào, (2) hoặc chỉ có con trái, (3)
hoặc chỉ có con phải, (4) hoặc có cả con trái và con phải
• Mỗi nút đều có dữ liệu (data), một con trỏ trỏ đến con trái và một con trỏ trỏ
đến con phải.

Con phải của a


Con trái của a
Data
left right
pointer pointer

94
Chú ý
a
a a

b b b

c d c d c d

e
e e

Cây nhị phân T1 Cây nhị phân T2 Cây tổng quát

T1 ≠ T2
Tính chất của cây nhị phân
height(cây) = depth(cây)
= MAX {depth(lá)} = height(gốc)
Bổ đề 1:
1) max # số nút trên mức i = 2i-1 (i ≥ 1)
i. max # số lá = 2depth(tree)-1
2) max # số nút = 2depth(tree) – 1
3) Cây nhị phân có n nút: depth(cây) ≥ log2(n+1)

h=5 Depth/level =1

h=4 l=2

h=3 l=3

h=2 l=4

Height= 1 l=5 96
Tính chất của cây nhị phân
Bổ đề 1:
1) max # số nút trên mức i = 2i-1 (i ≥ 1) : Mức i của cây nhị phân có số nút nhiều
nhất là 2i-1
Chứng minh: Bằng qui nạp theo i.
• Cơ sở: Gốc là nút duy nhất trên mức i=1. Như vậy số nút lớn nhất trên mức i=1
là 20= 2i-1.
• Chuyển qui nạp: Giả sử với mọi j, 1 ≤ j < i: số nút lớn nhất trên mức j là 2j-1.
– Do số nút trên mức i-1 là 2i -2, mặt khác theo định nghĩa mỗi nút trên cây nhị
phân có không quá 2 con
số lượng nút lớn nhất trên mức i ≤ 2 * số lượng nút trên mức i-1
2* 2i – 2
2i-1
Tính chất của cây nhị phân
Bổ đề 1:
2) max # số nút = 2depth(tree) – 1: cây nhị phân với chiều cao k có số nút nhiều
nhất là 2k-1, k ≥ 1.

Chứng minh:
Theo 1) có: Mức i của cây nhị phân có số nút nhiều nhất là 2i-1 (i ≥ 1)
Cây nhị phân chiều cao k  có k mức
 Tổng số nút trên cây nhều nhất chỉ có thể là
k

 2
i 1
i 1
 2 k
 1.
Tính chất của cây nhị phân
Bổ đề 1:
3) Cây nhị phân có n nút: depth(cây) ≥ log2(n+1)

Chứng minh:
• Cây nhị phân n nút có chiều cao thấp nhất k khi số lượng nút ở các
mức i =1, 2, ..., k đều là lớn nhất có thể được. Từ đó ta có:

k
n   2i 1  2k  1, suy ra 2k  n  1, hay k   log 2 (n  1)  .
i 1
Cây nhị phân đầy đủ (Full binary tree)
và Cây nhị phân hoàn chỉnh (Complete binary tree)
• Cây nhị phân đầy đủ: là cây nhị phân thỏa mãn 4
– Mỗi nút trong đều có 2 con,
– Mọi nút lá đều có cùng độ sâu 1 3
2 16 9 10

• Cây nhị phân hoàn chỉnh: là cây nhị phân thỏa mãn Full binary tree
– Tất cả các mức (có thể không kể đến mức sâu nhất) đều đầy
– Nếu mức sâu nhất không đầy, thì tất các nút ở mức này là lệch sang trái nhất có
thể được

100
Complete binary tree
Tìm cây nhị phân đầy đủ / cây nhị phân hoàn chỉnh

Cây nhị phân hoàn chỉnh Cây nhị phân đầy đủ Cây nhị phân hoàn chỉnh
và hoàn chỉnh

Không phải cây nhị phân đầy đủ Cây nhị phân đầy đủ Không phải cây nhị phân đầy đủ
Không phải cây nhị phân hoàn chỉnh và hoàn chỉnh Không phải cây nhị phân hoàn chỉnh
Cây nhị phân cân bằng (balanced binary tree)
• Định nghĩa. Cây nhị phân được gọi là cân bằng (balanced ) nếu chiều cao của
cây con trái và chiều cao của cây con phải của mọi nút chênh lêch nhau không
quá 1 đơn vị.
Cây nhị phân cân bằng (balanced binary tree)
• Định nghĩa. Cây nhị phân được gọi là cân bằng (balanced ) nếu chiều cao của
cây con trái và chiều cao của cây con phải của mọi nút chênh lêch nhau không
quá 1 đơn vị.
• Nhận xét:
– Nếu cây nhị phân là đầy đủ thì nó là hoàn chỉnh
– Nếu cây nhị phân là hoàn chỉnh thì nó là cân bằng
Ví dụ:

1. Cây nào là đầy đủ?


2. Cây nào là hoàn chỉnh?
3. Cây nào là cân đối?
True or False?
a) Nếu cây nhị phân là đầy đủ thì nó là cân bằng

b) Nếu cây nhị phân là hoàn chỉnh thì nó là đầy đủ

c) Nếu cây nhị phân là cân bằng thì nó là hoàn chỉnh

d) Nếu cây nhị phân là cân bằng thì nó là đầy đủ


4. Cây nhị phân (Binary tree)
4.1. Định nghĩa và tính chất
4.2. Biểu diễn cây nhị phân
4.3. Duyệt cây nhị phân
4.4. Một số ứng dụng

105
4.2. Biểu diễn cây nhị phân
4.2.1. Mảng
4.2.2. Con trỏ

106
Mảng 1 chiều biểu diễn cây nhị phân
Các nút trên cây được đánh chỉ số như sau:
• Chỉ số 0: nút gốc,
• Tất cả các nút còn lại được đánh theo thứ tự từ trái sang phải, từ trên xuống dưới.
Các nút rỗng cũng được đánh số.
Khi đó, nút được đánh chỉ số i sẽ được lưu vào thành phần thứ i của mảng.
 Mảng 1 chiều A dùng để biểu diễn cây nhị phân như sau:
• Gốc của cây là A[0]
• Với mỗi nút tại A[i]:
– Nút cha của nó là A[(i-1)/2]
– Nút con trái của nó là A[2i + 1] và con phải của nó là A[2i + 2]
– Nếu nút không có con nào, thì các giá trị null sẽ được lưu tại các thành phần
tương ứng trên mảng.

H D K B F J NULL NULL NULL A NULL NULL C NULL

A 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ………………… 25 26 ….. 30

Con phải của K Con trái của J Con trái của C Con phải của C
Con trái của B
Con phải của B 107
Mảng 1 chiều biểu diễn cây nhị phân
Depth/level =1 Max #nút = 20=1

l=2 Max #nút = 21=2

l=3 Max #nút = 22=4

l=4 Max #nút = 23=8

Max #nút = 24=16


 

𝑚𝑎𝑥#𝑛ú𝑡 = 31
 
Mảng 1 chiều biểu diễn cây nhị phân hoàn chỉnh

H D K B F J C P E G

A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9]

Sử dụng mảng 1 chiều biểu diễn cây nhị phân hoàn chỉnh n nút:
• Ta chỉ cần mảng 1 chiều gồm n phần tử
• Kiểm tra A[i] có phải là nút lá hay không ?
109
Mảng 1 chiều biểu diễn cây nhị phân
[0] A (1) Lãng phí bộ nhớ [0] A
A [1] B (2) thêm/xóa phức tạp
[1] B
[2] NULL
[2]
A C
B [3] C D
[3]
[4] NULL
[4] E
C [5] NULL
B C [5] F
[6] NULL
[6] G
D [7] D
[7] H
D E F G [8] L
NULL
E
[15] E H L

110
4.2. Biểu diễn cây nhị phân
4.2.1. Mảng
4.2.2. Con trỏ

111
Sử dụng con trỏ để biểu diễn cây nhị phân
• Mỗi nút của cây sẽ có con trỏ trỏ đến con trái và con trỏ trỏ đến con phải.
• Nếu nút không có con trái hoặc con phải, thì con trỏ trỏ đấy con tương ứng
sẽ chứa giá trị NULL.
• Nút lá con trỏ trái và con trỏ phải đều = NULL vì nó không có con nào.
• Cấu trúc một nút trên cây nhị phân được định nghĩa trên ngôn ngữ C như
sau:
typedef struct
{
DataType data; /*data: dữ liệu của nút; DataType: int, char,
double..*/
struct node *left ; /* trỏ đến nút con trái */
struct node *right; /* trỏ đến nút con phải */
}node;

Trỏ đến nút con trái Trỏ đến nút con phải
data

112
Sử dụng con trỏ để biểu diễn cây nhị phân: Ví dụ
A

B A

E C
B C D
F G D

H E F G

J
H I J K
K
4. Cây nhị phân (Binary tree)
4.1. Định nghĩa và tính chất
4.2. Biểu diễn cây nhị phân
4.3. Duyệt cây nhị phân
4.4. Một số ứng dụng

114
4.3. Duyệt cây nhị phân
• Duyệt cây tức là mỗi nút trên cây đều được thăm và
thăm đúng 1 lần
• Có hai cách duyệt cây thông dụng
– Duyệt theo chiều rộng (Breadth First Search)
– Duyệt theo chiều sâu (Depth First Search)
4.3. Duyệt cây nhị phân: theo chiều rộng
• Duyệt cây theo chiều rộng: thăm nút gốc trên mức 1, rồi thăm
đến các nút trên nút 2, rồi đến các nút trên mức 3, ...
• Các nút trên cùng 1 mức được thăm theo thứ tự từ trái sang phải

2 Depth/level =1

4 5 l=2

7 3 10 8 l=3

1 9 l=4
11

6 l=5

• Breadth_First_Search(2): 2, 4, 5, 7, 3, 10, 8, 1, 9, 11, 6


4.3. Duyệt cây nhị phân: theo chiều sâu
• Duyệt cây theo chiều sâu: lần lượt thăm các nút theo từng
nhánh
• Có ba loại duyệt cây theo chiều sâu
– Thứ tự giữa Inorder
– Thứ tự trước Preorder
– Thứ tự sau Postorder
• Mỗi loại duyệt đều có ứng dụng thực tế tương ứng với nó
4.3. Duyệt cây nhị phân: theo chiều sâu
• Thứ tự trước Preorder NLR
– Thăm nút (Visit a node),
– Thăm cây con trái theo thứ tự trước (Visit left subtree in preorder),
– Thăm cây con phải theo thứ tự trước (Visit right subtree in preorder)
• Thứ tự giữa Inorder LNR
– Thăm cây con trái theo thứ tự giữa (Visit left subtree in inorder),
– Thăm nút (Visit a node),
– Thăm cây con phải theo thứ tự giữa (Visit right subtree in inorder)
• Thứ tự sau Postorder LRN
– Thăm cây con trái theo thứ tự sau (visit left subtree in postorder),
– Thăm cây con phải theo thứ tự sau (isit right subtree in postorder),
– Thăm nút (Visit a node)

118
Duyệt thứ tự trước (Preorder traversal – NLR)
• Thăm nút (Visit the node).
• Duyệt cây con trái (Traverse the left subtree).
• Duyệt cây con phải (Traverse the right subtree).

void printPreorder(node *root)


{ if( root != NULL )
{
printf("%s ", root->word);
printPreorder(root->left);
printPreorder(root->right);
}
}
Gọi: printPreorder(A);
A B D E F G H C L J M

Cây con trái Cây con phải


119
Duyệt thứ tự giữa (Inorder traversal – LNR)
• Duyệt cây con trái (Traverse the left subtree)
• Thăm nút (Visit the node)
• Duyệt cây con phải (Traverse the right subtree)

void printInorder(node *tree)


{
if( tree != NULL )
{
printInorder(tree->left);
printf("%s ", tree->word);
printInorder(tree->right);
}
}
Gọi: printInorder(A);
E D G F B H A L C M J

120
Cây con trái Cây con phải
Duyệt thứ tự sau (Postorder traversal – LRN)
• Duyệt cây con trái (Traverse the left subtree)
• Duyệt cây con phải (Traverse the right subtree)
• Thăm nút (Visit the node)

void printPostorder(node *tree)


{
if( tree != NULL )
{
printPostorder(tree->left);
printPostorder(tree->right);
printf("%s ", tree->word);
}
}
Gọi: printPostorder(A);
E G F D H B L M J C A

Cây con trái Cây con phải 121


Các thao tác cơ bản trên cây nhị phân
typedef struct
{
char word[20]; // dữ liệu của nút
struct node * left;
struct node *right;
}node;

node* makeTreeNode(char *word);


node *insertNode(node* root, char *word, bool LEFT);
int countNodes(node *root);
int depth(node *root);
void freeTree(node *root);
void printPreorder(node *root);
void printPostorder(node *root);
void printInorder(node *root); 122
Các thao tác cơ bản trên cây nhị phân: MakeNode
node* makeTreeNode(char *word);
• Tham số đầu vào: dữ liệu của nút mới
• Đầu ra: hàm trả về con trỏ trỏ tới nút mới được tạo (tức là địa chỉ của nút được tạo)
Các bước phải thực hiện:
• Cấp phát bộ nhớ cho nút mới. Kiểm tra cấp phát bộ nhớ có thành công hay không?
– Nếu Yes: gán dữ liệu cho nút mới = word
con trỏ trỏ tới con trái = NULL
con trỏ tro tới con phải = NULL
• Trả về con trỏ trỏ tới nút mới (trả về địa chỉ của nút mới)

123
Các thao tác cơ bản trên cây nhị phân: thêm một nút mới vào cây
node *insertNode(node *root, char *word, bool LEFT)
{
node *newNode, *p;
newNode = makeTreeNode(word);
if ( root == NULL ) return newNode;
if (LEFT) //thêm nút vào vị trí trái nhất trên cây
{
p = root;
while (p->left !=NULL) p = p->left;
p->left = newNode;
printf("Nút %s là con trái của nút %s \n",word,(*p).word); C
}
else { //thêm nút vào vị trí phải nhất trên cây
Gọi: insertNode(H, “C”, 1);
p = root;
while (p->right !=NULL) p = p->right;
p->right = newNode;
printf("Nút %s là con phải của nút %s \n", word,(*p).word);
}
return root;
}
Các thao tác cơ bản trên cây nhị phân: thêm một nút mới vào cây
node *insertNode(node *root, char *word, bool LEFT)
{
node *newNode, *p;
newNode = makeTreeNode(word);
if ( root == NULL ) return newNode;
if (LEFT) //thêm nút vào vị trí trái nhất trên cây
{
p = root;
while (p->left !=NULL) p = p->left;
p->left = newNode;
printf("Nút %s là con trái của nút %s \n",word,(*p).word); C
}
else { //thêm nút vào vị trí phải nhất trên cây
Call: insertNode(H, “C”, 0);
p = root;
while (p->right !=NULL) p = p->right;
p->right = newNode;
printf("Nút %s là con phải của nút %s \n", word,(*p).word);
}
return root;
}
Các thao tác cơ bản trên cây nhị phân: tính chiều sâu của cây
int depth(node *root) { /* hàm tính chiều sâu của cây */
if( root == NULL ) return 0;
int lDepth = depth(root->left);
int rDepth = depth(root->right);
return max(lDepth, rDepth) + 1;
}
Gọi depth(H):

max (depth(D), depth(NULL)) + 1

max(max(depth(B), depth(F)),0) + 1

(max(depth(NULL), depth(NULL)) + 1)

max(depth(NULL), depth(NULL)) + 1
Các thao tác cơ bản trên cây nhị phân: đếm số nút có trên cây
int countNodes(node *root) {
/* hàm đếm số nút trên cây*/
if( root == NULL ) return 0;
A
else {
int ld = countNodes(root->left);
B
int rd = countNodes(root->right);
return 1+ld+rd;
E C
}
} F G D

#nút trên cây = 1 + #nút trên cây con trái + #nút trên cây con phải
Gọi countNodes(A);
Các thao tác cơ bản trên cây nhị phân: xóa cây
void freeTree(node *root)
{
if (root == NULL ) return;
freeTree(root->left);
freeTree(root->right);
free(root);
}
Program in C
/* The program for testing binary tree traversal */
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char word[20];
struct node * left;
struct node *right;
} node;
node *makeTreeNode(char *word);
node *insertNode(node* root,char *word);
void freeTree(node *tree);
void printPreorder(node *tree);
void printPostorder(node *tree);
void printInorder(node *tree);
int countNodes(node *tree);
int depth(node *tree);
Program in C
void main() {
node *randomTree=NULL;
char word[20] = "a";
while( strcmp(word,"~")!=0)
{ printf("\nEnter item (~ to finish): ");
scanf("%s", word);
if (strcmp(word,"~")==0) printf(“Finish to input data for node... \n");
else randomTree = insertNode(randomTree,word, rand()%2);
}
printf("The tree in preorder:\n"); printPreorder(randomTree);
printf("The tree in postorder:\n"); printPostorder(randomTree);
printf("The tree in inorder:\n"); printInorder(randomTree);
printf("The number of nodes is: %d\n",countNodes(randomTree));
printf("The depth of the tree is: %d\n", depth(randomTree));
freeTree(randomTree);
}
Bài tập
• Vẽ cây nhị phân có chiều cao = 3, kết quả duyệt thứ tự sau
là (10, 30, 50, 20, 40, 70, 60)

Postorder: Left_Right_Root
4. Cây nhị phân (Binary tree)
4.1. Định nghĩa và tính chất
4.2. Biểu diễn cây nhị phân
4.3. Duyệt cây nhị phân
4.4. Một số ứng dụng

132
4.4. Một số ứng dụng
4.4.1. Biểu thức số học
4.4.2. Mã Huffman
4.4.3. Cây quyết định

133
4.4. Một số ứng dụng
4.4.1. Biểu thức số học
4.4.2. Mã Huffman
4.4.3. Cây quyết định

134
4.4.1. Biểu thức số học
• Cây biểu thức là cây nhị phân được sử dụng để biểu diễn biểu thức
số học, trong đó:
– Các nút trong: các phép toán
– Các nút lá: các toán hạng
Ví dụ 1: cây biểu thức biểu diễn biểu thức ((2  (5 - 1)) + (3  2))
Duyệt cây:
 1. Theo thứ tự trước (preorder traversal)
+x2–51x32
  Cho ta: kí pháp tiền tố (prefix expression)
2. Theo thứ tự giữa (inorder traversal)
2  3 2 2x5–1+3x2
Cho ta: kí pháp trung tố (infix expression)
3. Theo thứ tự sau (postorder traversal)
5 1 251–x32x+
Cho ta: kí pháp hậu tố (postfix expression)
4. Theo mức (breadth first traveral)
+xx2–3251 135
4.4.1. Biểu thức số học
Ví dụ 2:  Duyệt cây:
1. Theo thứ tự trước (preorder traversal)
* E +**/ABCDE
Cho ta: kí pháp tiền tố (prefix expression)
* 2. Theo thứ tự giữa (inorder traversal)
D
A/B*C*D+E
Cho ta: kí pháp trung tố (infix expression)
/ C 3. Theo thứ tự sau (postorder traversal)
AB/C*D*E+
A B Cho ta: kí pháp hậu tố (postfix expression)
4. Theo mức (breath first traveral)
+*E*D/CAB

• Mức (độ sâu) của các nút trên cây cho biết trình tự thực hiện chúng (ta không
cần sử dụng ngoặc để chỉ ra trình tự):
– Phép toán tại mức cao hơn của cây được thực hiện muộn hơn so với các
mức dưới chúng.
– Phép toán ở gốc luôn được thực hiện sau cùng. 136
Kí pháp trung/hậu/tiền tố
(Infix/Postfix/Prefix Expressions)
• Trung tố: là biểu thức trong đó các toán hạng đặt bao quanh các phép
toán
Ví dụ: x+y, x+y*z
• Hậu tố (còn gọi là Reverse Polish Notation/ Ký Pháp Nghịch Đảo Ba
Lan): các phép toán được đặt sau các toán hạng
Ví dụ: xy+ , xyz*+
• Tiền tố (còn gọi là Polish notation): các phép toán được đặt trước các
toán hạng
Ví dụ: +xy, +x*yz

x *
y z 137
Kí pháp trung/hậu/tiền tố
(Infix/Postfix/Prefix Expressions)
• Khi lập trình, việc để cho máy tính tính giá trị một biểu thức toán học là điều
quá đỗi bình thường, nhưng để trình bày làm sao cho máy tính có thể đọc và
hiểu được quy trình tính toán đó không phải là điều đơn giản.
– Ví dụ: “giải quyết” các biểu thức có dấu ngoặc như (a+b)*c + (d+e)*f , thì các
phương pháp tách chuỗi đơn giản đều không khả thi. Trong tình huống này, ta
phải dùng đến Ký Pháp Nghịch Đảo Ba Lan (Reserve Polish Notation – RPN),
một thuật toán “kinh điển” trong lĩnh vực trình biên dịch.
– Ví dụ: 5 + ((1 + 2) * 4) + 3  5 1 2 + 4 * + 3 +

Trung tố Hậu tố / Kí pháp nghịch đảo Ba Lan

Ký pháp nghịch đảo Ba Lan được phát minh vào khoảng giữa thập kỷ 1950
bởi Charles Hamblin – một triết học gia và khoa học gia máy tính người Úc –
dựa theo công trình về ký pháp Ba Lan của nhà Toán học người Ba Lan Jan
Łukasiewicz. Hamblin trình bày nghiên cứu của mình tại một hội nghị khoa
học vào tháng 6 năm 1957 và chính thức công bố vào năm 1962.
138
Tính giá trị kí pháp hậu tố
636+5*9/-

Quá trình tính toán giá trị của biểu thức hậu tố khá tự nhiên đối với máy
tính. Ý tưởng như sau: đọc biểu thức từ trái sang phải:
• nếu gặp toán tử (kí hiệu OPT), thì lấy hai toán hạng ngay trước nó
trong biểu thức (kí hiệu lần lượt là A và B) …. A B OPT…
Và thực hiện phép tính C = A OPT B, rồi đẩy C vào lại biểu thức
Khi quá trình kết thúc thì con số cuối cùng còn lại trong biểu thứcchính
là giá trị của biểu thức đó.
Tính giá trị kí pháp hậu tố: sử dụng stack
636+5*9/-

Quá trình tính toán giá trị của biểu thức hậu tố khá tự nhiên đối với máy
tính. Ý tưởng như sau: đọc biểu thức từ trái sang phải:
• nếu gặp một toán hạng (con số hoặc biến) thì push toán hạng này vào
ngăn xếp;
• nếu gặp toán tử, lấy hai toán hạng ra khỏi ngăn xếp (stack), tính kết
quả, đẩy kết quả trở lại ngăn xếp.
Khi quá trình kết thúc thì con số cuối cùng còn lại trong ngăn xếp chính
là giá trị của biểu thức đó.
Ví dụ 1: Tính giá trị kí pháp hậu tố: sử dụng stack
• Tính giá trị kí pháp hậu tố sử dụng stack: 636+5*9/-
Quá trình tính toán thực hiện trên stack lần lượt như sau:
Element = 6 STACK Element = 3 STACK Element = 6 STACK Element = + STACK

Tính 3+6=9

Push 6 Pop 6
6
Push 3 Pop 3
3 3
Push 6
6 6 6 6

Push 9
Ví dụ 1: Tính giá trị kí pháp hậu tố: sử dụng stack
• Tính giá trị kí pháp hậu tố sử dụng stack: 636+5*9/-
Quá trình tính toán thực hiện trên stack lần lượt như sau:

STACK Element = 5 STACK Element = * STACK

Tính 9*5=45
Push 5 Pop 5
5 5
Push 9 Pop 9
9 9 9
6 6 6
Tính giá trị kí pháp hậu tố sử dụng stack: 6 3 6 + 5 * 9 / -
STACK Element = 9 STACK Element = / STACK

Tính 45/9=5

Push 9 Pop 9
9 9
Push 45 Pop 45
45 45 45
6 6 6

STACK Element = - STACK STACK

Tính 6-5=1
Push 5 Pop 5
5 5
Pop 6 Push 1
6 6 1
Trạng thái cuối cùng của stack chứa kết quả của biểu thức
Tính giá trị kí pháp hậu tố sử dụng stack
evaluationPostfix(stack S, postfixExpression E)
{ //Biểu thức được lưu ở mảng E
//Đọc các kí tự trong biểu thức từ trái sang phải:
i = 0;
while ( i < số_kí_tự_trong_biểu_thức)
{
if (E[i] là toán hạng)
push E[i] vào stack
else if (E[i] là phép toán)
{
1. Pop phần tử đầu stack và lưu vào biến operand1
2. Pop phần tử đầu stack và lưu vào biến in operand2
3. Tính: operand2 op operand1 {op = E[i]}, rồi push kết quả vào
stack
}
i = i + 1;
}//end while
Pop phần tử đầu stack, lưu vào biến Result;
return Result; //chứa kết quả của biểu thức
}

144
Ví dụ 1:Tính kí pháp hậu tố: Kí tự của biểu thức Thao tác thực hiện Trạng thái Stack
636+5*9/- 1 6 Push 6 vào stack 6
2 3 Push 3 vào stack 6 3
3 6 Push 6 vào stack 6 3 6
4 + Pop 6 6 3
5 Pop 3 6
6 Tính 3 + 6 = 9 6
7 Push 9 vào stack 6 9
8 5 Push 5 vào stack 6 9 5
9 * Pop 5 6 9
10 Pop 9 6
11 Tính 9*5=45 6
Giá trị biểu thức = 1 12 Push 45 vào stack 6 45
13 9 Push 9 vào stack 6 45 9
14 / Pop 9 6 45
15 Pop 45 6
16 Tính 45/9=5 6
17 Push 5 vào stack 6 5
18 - Pop 5 6
19 Pop 6 Empty
20 Tính 6-5=1 Empty
21 Push 1 vào stack 1 145
22 Pop value = 1 Empty
Ví dụ 1: Tính giá trị kí pháp hậu tố: dùng stack
6 3 6 + 5 * 9 / -

3 +6 = 9
6 9 5 * 9 / -

9 * 5 = 45 Đọc biểu thức từ trái sang phải


6 45 9 / -

45 / 9 = 5
6 5 -

6–5=1

Tính giá trị biểu thức = 1


146
Ví dụ 2: Tính giá trị kí pháp tiền tố: sử dụng stack
+ - * 2 3 5 / * 2 3 2
2*3 =6

+ - * 2 3 5 / 6 2

6/2=3 Đọc biểu thức từ phải sang trái


+ - * 2 3 5 3

2*3=6

+ - 6 5 3

6–5=1
+ 1 3

1+3=4
147
Giá trị biểu thức = 4
Ví dụ 2: Tính giá trị kí pháp tiền tố: sử dụng stack
+ - * 2 3 15 / * 2 3 12
2*3 =6

+ - * 2 3 15 / 6 12

6 / 12 = 0.5 Đọc biểu thức từ phải sang trái


+ - * 2 3 15 0.5

2*3=6

+ - 6 15 0.5

6 – 15 = -9
+ (-9) 0.5

(-9) + 0.5 = -8.5


148
Giá trị biểu thức = -8.5
Ví dụ 2: Tính giá trị kí pháp tiền tố:
+-*235/*232
Kí tự của Thao tác thực hiện Trạng thái stack Kí tự của Thao tác thực hiện Trạng thái Stack
biểu thức biểu thức
1 2 Push 2 to stack 2 15 * Pop 2 3 5 3
2 3 Push 3 to stack 2 3 16 Pop 3 3 5
3 2 Push 2 to stack 2 3 2 17 Evaluate 2*3=6 3 5
4 * Pop 2 2 3 18 Push 6 to stack 3 5 6
5 Pop 3 2 19 - Pop 6 3 5
6 Evaluate 2 * 3 = 6 2 20 Pop 5 3
7 Push 6 to stack 2 6 21 Evaluate 6-5 = 1 3
8 / Pop 6 to stack 2 22 Push 1 to stack 3 1
9 Pop 2 Empty 23 + Pop 1 3
10 Evaluate: 6/2=3 Empty 24 Pop 3 Empty
11 Push 3 to stack 3 25 Evaluate 1 + 3 = 4 Empty
12 5 Push 5 to stack 3 5 26 Push 4 to stack 4
13 3 Push 3 to stack 3 5 3 27 Pop value = 4 Empty
14 2 Push 2 to stack 3 5 3 2

Giá trị biểu thức = 4


149
Chuyển đổi từ trung tố sang hậu tố
• Đã biết cách tính giá trị kí pháp hậu tố: sử dụng stack
• Làm thế nào để chuyển từ kí pháp trung tố sang kí pháp hậu tố ?

150
Chuyển đổi từ trung tố sang hậu tố: ứng dụng Stack
EH là biểu thức hậu tố cần xây dựng. Khởi tạo EH =
Bước 1: Duyệt biểu thức từ trái sang phải: kí tự hiện thời là x
• Nếu x là toán hạng thì viết vào bên phải EH.
• Nếu x là dấu mở ngoặc ( thì đẩy nó vào Stack
• Nếu x là phép toán thì đưa x vào Stack theo cách sau:
a. Nếu Stack rỗng hoặc là dấu mở ngoặc ( thì đẩy x vào.
b. Nếu phép toán y ở đỉnh Stack có độ ưu tiên thấp hơn x thì đẩy x vào Stack.
c. Nếu y ở đỉnh Stack có độ ưu tiên lớn hơn hoặc bằng độ ưu tiên của x thì lấy y ra
khỏi Stack, viết y bên phải EH. Quay lại bước (a).
• Nếu x là dấu đóng ngoặc ) thì:
a. Nếu đỉnh Stack là phép toán y thì lấy y ra khỏi Stack và viết vào bên phải EH. Lặp
lại quá trình cho tới khi Stack rỗng hoặc đỉnh Stack là dấu mở ngoặc. Khi gặp dấu
mở ngoặc thì loại nó ra khỏi Stack.
b. Nếu ở đỉnh Stack là dấu mở ngoặc thì loại dấu mở ngoặc ra khỏi đỉnh ngăn xếp.
Bước 2: Khi đã đọc hết các thành phần của biểu thức thì ta lần lượt lấy các phần tử từ đỉnh
Stack và viết vào bên phải EH cho đến khi Stack rỗng.
151
In biểu thức
Cho cây nhị phân có con trỏ root trỏ đến gốc của cây biểu diễn một biểu thức số học.
Viết hàm in ra biểu thức đó.
• Duyệt cây theo thứ tự giữa:
– In kí tự “(“ trước khi thực hiện duyệt cây con trái
– In ra toán hạng hoặc phép toán có ở trong nút khi thực hiện thăm nút
– In kí tự “)“ sau khi thăm xong cây con phải
void printTree(node *root)
 if (root.left != NULL)
{
  printf("(");
 printTree (root.left);
2 3 2
}
print(root.data);
5 1
if (root.right != NULL)
Gọi printTree(+); {
Biểu thức in ra là: printTree (root.right);
((2  (5 - 1)) + (3  2)) printf (")");
}
Tính giá trị biểu thức số học
Cho cây nhị phân có con trỏ root trỏ vào gốc của cây biểu diễn một biểu thức ố
học. Hãy viết hàm tính giá trị của biểu thức này.
• Duyệt cây theo thứ tự sau (Postorder traversal):
– Tính giá trị các cây con một cách đệ quy
– Thực hiện phép toán ở nút gốc sau khi các cây con trái và phải của nó đã
được tính xong giá trị.

int evaluate (node *root)
  {
if (root->left == NULL)//external node
2  3 2 return root->data;
else { //internal node
x = evaluate (root->left);
5 1 y = evaluate (root->right);
gọi OPT là phép toán ở nút gốc root
z = x OPT y
return z;
Gọi A = evaluate(+);
}
Giá trị của A là: }
Bài tập: Tính giá trị biểu thức sử dụng stack
Bài tập 1: tính giá trị các kí pháp hậu tố
1) 2 3 + 4 * 5 * Cách 1: Miêu tả bẳng cách vẽ lần lượt trạng thái stack:

2) 27 3 2 ^ / 3 17 * + 27 2 * -
3) 7 6 + 4 * 410 - 5 ^
4) 7 5 – 9 2 / *

Bài tập 2: tính giá trị các kí pháp tiền tố


1) - + 7 * 4 5 + 2 10
2) - * 9 + 2 3 / 27 3 Cách 2: Miêu tả các bước của thuật toán bằng cách kẻ bảng:

Cách 3:
4.4. Một số ứng dụng
4.4.1. Biểu thức số học
4.4.2. Mã Huffman
4.4.3. Cây quyết định

155
Bài tập
• Giả sử ta cần lưu trữ một file gồm 100,000 kí tự. File này chỉ chứa 6 kí tự, với tần suất xuất
hiện như sau:
a b c d e f

Frequency 45000 13000 12000 16000 9000 5000

• Ta sẽ mã hóa mỗi kí tự bởi một xâu nhị phân (gọi là codeword). Do đó, ta cần tìm một dãy
các bit nhị phân mã hóa file này sao cho số bit sử dụng là ít nhất có thể, tức là tìm cách mã
hóa sao cho sử dụng bộ nhớ ít nhất.
• Có hai cách mã hóa:
– Mã hóa với độ dài cố định (fixed-length code): các codeword đều có độ dài bằng nhau.
– Mã hóa với độ dài biến đổi (variable-length code): các codeword có độ dài khác nhau.
• Ví dụ: nếu sử dụng mã với độ dài cố định, thì với file văn bản đã cho, với 6 kí tự, ta cần ít
nhất 3 bit cho mỗi codeword (vì 22 = 4 < 6, 23 = 8)
a b c d e f
Frequency 45000 13000 12000 16000 9000 5000
A fixed-length 000 001 010 011 100 101
A variable-length 0 101 100 111 1101 1100
Bài tập
• Ví dụ: nếu sử dụng mã với độ dài cố định, thì với file văn bản đã cho, với 6 kí tự, ta cần ít
nhất 3 bit cho mỗi codeword (vì 22 = 4 < 6, 23 = 8)
a b c d e f
Frequency 45000 13000 12000 16000 9000 5000
A fixed-length 000 001 010 011 100 101
A variable-length 0 101 100 111 1101 1100

• Mã hóa với độ dài cố định (fixed length-code) cần:

3 * 100000 = 300000 bits để lưu trữ file đã cho


3 bit cho mỗi kí tự Số lượng kí tự trong file

• Mã hóa với độ dài thay đổi (variable-length code) cần:

1 * 45000 + 3*13000+ 3*12000 + 3 * 16000 + 4 * 9000 + 4 * 5000 = 224000 bit


#bit sử dụng để mã hóa kí tự “a” #bit sử dụng để mã hóa kí tự “f”

 Tiết kiệm bộ nhớ: 25%


Bài tập
Có thể mã hóa bằng 2 cách:
• Mã hóa với độ dài cố định (fixed-length code): các codeword có độ dài bằng nhau
 dễ mã hóa cũng như giải mã, nhưng lại đòi hỏi bộ nhớ lớn.
• Mã hóa với độ dài biến đổi (variable-length code): các codeword có thể có độ dài khác
nhau
– Kí tự có tần suất xuất hiện nhiều nhất (ít nhất) sẽ được mã hóa bởi codeword có độ
dài ngắn nhất (dài nhất).
– Mỗi codeword phải được xác định duy nhất, không phụ thuộc vào độ dài của nó
không có codeword nào là đoạn đầu của một codeword khác (ví dụ: {a = 1, b = 110,
c = 10, d = 111}, thì khi giải mã “1101111” ta thu được xâu ?)
 Mã phi tiền tố (Prefix code): là cách mã hóa mỗi kí tự c bởi một xâu nhị phân
code(c) sao cho mã của một kí tự bất kì không là đoạn đầu của bất cứ mã của ký tự nào
trong số các kí tự còn lại (ví dụ: {a = 0, b = 110, c = 10, d = 111} là mã phi tiền tố

a b c d e f
Frequency 45000 13000 12000 16000 9000 5000
A fixed-length 000 001 010 011 100 101
A variable-length 0 101 100 111 1101 1100
Bài toán mã hóa tối ưu (Optimum source coding problem)

• Mã hóa với độ dài biến đổi sử dụng 224000 bit thay vì 300000 bits nếu sử dụng
mã hóa với độ dài cố định  tiết kiệm bộ nhớ 25%. Câu hỏi: có cách mã hóa
nào tốt hơn không ? Hay đây đã là cách mã hóa tối ưu (cho chi phí nhỏ nhất) ?
a b c d e f
Frequency 45000 13000 12000 16000 9000 5000
A fixed-length 000 001 010 011 100 101
A variable-length 0 101 100 111 1101 1100

• Bài toán mã hóa tối ưu: Cho bảng kí tự A = {a1, . . . , an} với tần suất xuất hiện
f(ai), hãy tìm mã phi tiền tố C cho bảng kí tự A sao cho số lượng bit cần sử dụng
là nhỏ nhất

Cần để mã hóa file gồm kí tự, với:


– là xâu nhị phân để mã hóa kí tự ,
– là độ dài của .
Bài toán mã hóa tối ưu (Optimum source coding problem)

• Bài toán mã hóa tối ưu: Cho bảng kí tự A = {a1, . . . , an} với tần suất xuất hiện
f(ai), hãy tìm mã phi tiền tố C cho bảng kí tự A sao cho số lượng bit cần sử dụng
là nhỏ nhất

Cần để mã hóa file gồm kí tự, với:


– là xâu nhị phân để mã hóa kí tự ,
– là độ dài của . David A. Huffman
1925-1999

Huffman đã đề xuất một thuật toán tham lam để giải quyết bài toán mã
hóa tối ưu, tức là xây dựng được một mã phi tiền tố với tổng số bit sử
dụng là ít nhất. Mã đó được gọi là mã Huffman.
4.4.2 Mã Huffman
Thuật toán:
• Bước 1: Lấy 2 kí tự có tần suất xuất hiện nhỏ nhất x, y từ bảng kí tự A, rồi
xây dựng cây con gồm 2 nút lá chứa 2 kí tự này (ý tưởng tham lam). Gán
nhãn cho nút gốc của cây này là z.
• Bước 2: Đặt tần suất xuất hiện f(z) = f(x) + f(y). Xóa x, y và thêm z tạo nên
bảng kí tự mới: khi đó |
Lặp lại 2 bước trên, đặt tên là ghép, với bảng kí tự mới cho đến khi bảng kí
tự chỉ còn gồm 1 kí tự.
Cây thu được chính là mã Huffman
4.4.2 Mã Huffman
• Cho A = {a / 20, b / 15, c / 5, d /15, e / 45} là bảng kí tự cùng tần suất xuất
hiện của các kí tự.
• Bước thứ 1: mã Huffman sẽ ghép c và d

Bảng kí tự mới A1 = {a / 20, b / 15, n1 / 20, e / 45}


• Thuật toán tiếp tục ghép a và b (hoặc có thể ghép b và n1)

Bảng kí tự mới A2 = {n2 / 35, n1: / 20, e / 45}


4.4.2 Mã Huffman
Bảng kí tự mới A2 = {n2 / 35, n1 / 20, e / 45}
Ghép n1 và n2:

Bảng kí tự mới A3 = {n3 / 55, e / 45}


Ghép e và n3, và thuật toán kết thúc

Mã Huffman thu được:


a = 000, b = 001, c = 010, d = 011, e = 1
Là mã phi tiền tố tối ưu
(cho tổng số bít sử dụng ít nhất)
4.4.2 Mã Huffman
• Tần suất của các kí tự trong văn bản: Char Freq
E 125
T 93
A 80
O 76
I 72
N 71
S 65
R 61
H 55
L 41
D 40
C 31
U 27
4.4.2 Mã Huffman

A O T E

80 76 93 125

D L R S N I H

40 41 61 65 71 73 55

C U
31 27
4.4.2 Mã Huffman

A O T E

80 76 93 125

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

A O T E

80 76 81 93 125

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

A O T E

80 76 81 93 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

A O T E

80 76 81 93 126 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

A O T E

80 76 81 93 126 144 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

156

A O T E

80 76 81 93 126 144 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

156 174

A O T E

80 76 81 93 126 144 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

156 174
238
A O T E

81 126 144 113

D L R S N I H

58

C U
4.4.2 Mã Huffman

156 174 270


238
A O T E

80 76 81 93 126 144 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

330

156 174 270


238
A O T E

80 76 81 93 126 144 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

330 508

156 174 270


238
A O T E

80 76 81 93 126 144 125 113

D L R S N I H

40 41 61 65 71 73 58 55

C U
31 27
4.4.2 Mã Huffman

0 1
838

0 330 1 0 508 1

0 174 1 0 1 0 1
0 156 1 270
238
A O T E

80 76 93 0 126 0 144 125 0 113 1


0 81 1 1 1

D L R S N I H

40 41 61 65 71 73 0 58 1 55

C U
31 27
4.4.2 Mã Huffman
Char Frequency Fixed-length code Huffman code
E 125 0000 110
T 93 0001 011
A 80 0010 000
O 76 0011 001
I 73 0100 1011
N 71 0101 1010
S 65 0110 1001
R 61 0111 1000
H 55 1000 1111
L 41 1001 0101
D 40 1010 0100
C 31 1011 11100
U 27 1100 11101
Sum 838 3352 3036

• Sử dụng mã Huffman: tiết kiệm bộ nhớ 10%


• Mã Huffman là ví dụ đơn giản của mã hóa dữ liệu
4.4.2 Mã Huffman: giải mã
• Thuật toán giải mã:
– Đọc file nén & xây dựng cây nhị phân
– Sử dụng cây nhị phần để giải mã
• Đi từ nút gốc đến nút lá
• Ví dụ: Giải mã “11100000011”
4.4.2 Mã Huffman: giải mã
procedure Huffman_Decode(B)
//B là xâu mã hóa văn bản theo mã Huffman
begin
<Khởi tạo P là gốc của cây Huffman>
While <chưa đạt đến kết thúc của B> do
begin
x  bit tiếp theo trong xâu B;
If x = 0 then P  Con trái của P
Else P  Con phải của P
If (P là nút lá) then
begin
<Hiển thị kí hiệu tương ứng với nút lá>
<Đặt lại P là gốc của cây Huffman>
end;
end;
end;
4.4. Một số ứng dụng
4.4.1. Biểu thức số học
4.4.2. Mã Huffman
4.4.3. Cây quyết định (Decision tree)

181
4.4.3. Cây quyết định (Decision Tree)
• Cây quyết định là một cây mà mỗi đỉnh trong của nó tương ứng với một truy
vấn đối với dữ liệu. Các cạnh của cây tương ứng với các khả năng trả lời cho
câu hỏi. Mỗi lá của cây tương ứng với một đầu ra.
• Để tính toán với cây quyết định chúng ta sẽ bắt đầu từ gốc và di chuyển theo
một đường đi đến lá. Tại mỗi nút trung gian câu trả lời cho truy vấn đối với
dữ liệu sẽ dẫn ta đến nút tiếp theo. Khi đạt đến lá ta thu được một đầu ra.
4.4.3. Cây quyết định
Ví dụ. Bài toán tra từ điển. Cho mảng A gồm n số được sắp theo thứ tự tăng dần và một số
x. Cần xác định xem x có mặt trong mảng đã cho hay không. Nếu câu trả lời là khẳng định
cần chỉ rõ vị trí của x trong mảng A.
• Cây quyết định để giải bài toán này có dạng một cây nhị phân:
– Mỗi nút trong của cây quyết định chứa một câu hỏi dạng: “x < k ?”
– Mỗi nút ở mức sát lá của cây quyết định chứa câu hỏi : “x = A[i]?”
có hai con, một con ứng với ‘i’ còn một con ứng với ‘-’ (không có).
Ví dụ: Xét A = (2, 3, 5, 7, 11, 13, 17, 19). Cây quyết định có dạng sau đây
4.4.3. Cây quyết định
• Ta xác định thời gian của thuật toán cây quyết định là số truy vấn
trên đường đi từ gốc đến lá. Như vậy, thời gian tính trong tình
huống xấu nhất của thuật toán sẽ là độ cao của cây quyết định.
• Nếu ta hạn chế chỉ được sử dụng phép so sánh để giải bài toán tra
từ điển, thì rõ ràng hoạt động của mọi thuật toán giải bài toán tra từ
điển đều có thể mô tả bởi cây quyết định.
• Do đó nếu đánh giá được cận dưới cho độ cao của cây quyết định
(thuật toán) giải bài toán tra từ điển thì ta cũng thu được cận dưới
cho độ phức tạp của bài toán.

184
4.4.3. Cây quyết định
• Giả thiết rằng: “Các truy vấn đối với dữ liệu phải đảm bảo đủ thông tin để có thể xác
định được đầu ra có thể”.
• Nếu bài toán có N đầu ra có thể thì rõ ràng mọi cây quyết định đều có N lá (không loại
trừ tình huống khi có một số lá tương ứng với cùng một đầu ra).
• Từ bổ đề 1, ta biết rằng: Nếu một cây có n lá và mỗi nút có nhiều nhất 2 nút con (mỗi
truy vấn có nhiều nhất 2 câu trả lời) thì độ cao của cây ít nhất sẽ là
log n = (log n)
• Áp dụng kết quả này: Với bài toán tra từ điển với tập S gồm n phần tử, do bài toán có tất
cả n+1 đầu ra có thể, nên mọi cây quyết định có n+1 lá, và vì thế, có độ cao ít nhất là
log (n+1) = (log n).
• Suy ra: Mọi thuật toán giải bài toán tra từ điển chỉ sử dụng phép so sánh đều có
thời gian tính là (log n).
• Như đã biết: Thuật toán tìm kiếm nhị phân có thời gian tính là O(log n). Từ các kết quả
trên suy ra: “Thuật toán tìm kiếm nhị phân là thuật toán nhanh nhất trong số các
thuật toán chỉ sử dụng phép so sánh để giải bài toán tra từ điển”. 185
Bài toán sắp xếp
• Cho dãy n số phân biệt (a[1], a[2], ..., a[n]), tìm hoán vị  = ((1), (2), ...,(n)) sao
cho: a[(1)] < a[(2)] < ... < a[(n)].
• Giả sử ta chỉ sử dụng phép so sánh để thực hiện sắp xếp, khi đó mọi thuật toán sắp
xếp đều có thể diễn tả trong mô hình cây quyết định. Cây quyết định là cây nhị phân
thoả mãn:
– Mỗi nút  một phép so sánh "a < b"
• cũng có thể coi tương ứng với một không gian con các trình tự
– Mỗi cạnh  rẽ nhánh theo câu trả lời (true hoặc false)
– Mỗi lá  1 trình tự sắp xếp
– Cây quyết định có bao nhiêu lá, nếu có n phần tử phân biệt cần sắp xếp?
• n!, tức là tất cả các trình tự có thể
• Đối với mỗi bộ dữ liệu, chỉ có 1 lá chứa trình tự sắp xếp cần tìm

186
Cây quyết định cho bài toán sắp xếp ba số a, b, c

a < b < c, b < c < a,


c < a < b, a < c < b,
b < a < c, c < b < a
a<b<c "a < b?" b<c<a
c<a<b a<b a>b
b<a<c
a<c<b c<b<a
"a < c?" "b < c?"
a<c a>c b<c b>c

a<b<c c<a<b b<c<a c<b<a


a<c<b b<a<c
"b < c?" "c < a?"
b<c b>c c<a c>a

a<b<c a<c<b b<c<a b<a<c

Mỗi lá tương ứng với một trình tự sắp xếp ba số a, b, c

187
Bài toán sắp xếp
• Do có tất cả n! hoán vị, nên mọi cây quyết định giải bài toán sắp xếp yêu
cầu ít nhất n! lá, vì thế có độ cao ít nhất là (log n!).
Ta có:
log n! = log 1+log 2 +...+ logn
≥ log(n/2) + log(n/2+1) +...+ log n
≥ (n/3) log(n/2)
= (1/3) (n log n - n log 2)
= (n log n).
• Như vậy, ta đã chứng minh được kết quả sau đây:
"Mọi thuật toán sắp xếp chỉ sử dụng phép so sánh đều đòi hỏi thời
gian (n log n)".

188

You might also like