3. Ch ng 5: Cây (Tree)ươ
Tree – Định nghĩa
3
Cây là một tập gồm 1 hay nhiều nút T, trong đó có một nút
đặc biệt được gọi là gốc, các nút còn lại được chia thành
những tập rời nhau T1, T2 , ... , Tn theo quan hệ phân cấp trong
đó Ti cũng là một cây
4. Ch ng 5: Cây (Tree)ươ
Tree – Ví dụ
4
Sơ đồ tổ chức của một công ty
Công ty ACông ty A
R&DR&D Kinh doanhKinh doanh Taøi vuïTaøi vuï Saûn xuaátSaûn xuaát
TVTV CDCD AmplierAmplierNoäi ñòaNoäi ñòa Quoác teáQuoác teá
Chaâu aâuChaâu aâu MyõMyõ Caùc nöôùcCaùc nöôùc
5. Ch ng 5: Cây (Tree)ươ
Tree – Ví dụ
Cây thư mục
5
7. Ch ng 5: Cây (Tree)ươ
Tree – Ví dụ
Không phải cây
7
Trong cấu trúc cây không tồn tại chu trình
8. Ch ng 5: Cây (Tree)ươ
Tree - Một số khái niệm cơ bản
Bậc của một nút (Degree of a Node of a Tree):
Là số cây con của nút đó. Nếu bậc của một nút bằng 0 thì nút đó
gọi là nút lá (leaf node)
Bậc của một cây (Degree of a Tree):
Là bậc lớn nhất của các nút trong cây. Cây có bậc n thì gọi là
cây n-phân
Nút gốc (Root node):
Là nút không có nút cha
Nút lá (Leaf node):
Là nút có bậc bằng 0
8
9. Ch ng 5: Cây (Tree)ươ
Tree - Một số khái niệm cơ bản
9
Nút nhánh:
Là nút có bậc khác 0 và không phải là gốc
Mức của một nút (Level of a node):
mức (gốc (T) ) = 0.
gọi T1, T2, T3, ... , Tn là các cây con của T0
mức (T1) = mức (T2) = ... = mức (Tn) = mức (T0) + 1.
Chiều cao của cây (độ sâu) (Height – Depth of a tree):
Là mức cao nhất của nút
11. Ch ng 5: Cây (Tree)ươ
Binary Tree – Định nghĩa
11
Cây nhị phân là cây mà mỗi nút có tối đa 2 cây con (cây có
bậc là 2)
Cây
con trái
Cây
con
phải
Hình ảnh một cây nhị phân
12. Ch ng 5: Cây (Tree)ươ
Binary Tree – Ví dụ
Cây lệch trái và cây lệch phải
13. Ch ng 5: Cây (Tree)ươ
Binary Tree – Ví dụ
Cây nhị phân đầy đủ (A full binary tree)
14. Ch ng 5: Cây (Tree)ươ
Binary Tree – Ứng dụng
Cây biểu thức: được dùng để biểu diễn một biểu thức toán học
14
15. Ch ng 5: Cây (Tree)ươ
Binary Tree – Một số tính chất
Số nút tại mức i ≤ 2i-1
Số nút lá ≤ 2h-1
, với h là chiều cao của cây
Số nút trong cây ≤ 2h
-1, với h là chiều cao của cây
Chiều cao của cây ≥ log2N, với N là số nút trong cây
15
16. Ch ng 5: Cây (Tree)ươ
Trắc nghiệm
Số nút lá trong cây nhị phân với chiều cao 4 là:
2 4 6 8
Chiều cao tối thiểu của cây nhị phân với 31 nút là bao nhiêu?
4 7 5 3
16
17. Ch ng 5: Cây (Tree)ươ
Binary Tree - Biểu diễn
17
Sử dụng cấu trúc để lưu trữ các thông tin của một nút gồm:
Dữ liệu của nút
Địa chỉ nút gốc của cây con trái
Địa chỉ nút gốc của cây con phải
Khai báo cấu trúc cây nhị phân:
typedef struct Node
{
DataType data;
Node *pLeft, *pRight;
};
typedef struct Tree
{
Node *pRoot;
};
18. Ch ng 5: Cây (Tree)ươ
Binary Tree – Khởi tạo cây
Khởi tạo cây rỗng:
void InitTree (Tree &t)
{
t.pRoot = NULL;
}
18
19. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
19
Có 3 kiểu duyệt chính có thể áp dụng trên cây nhị phân:
Duyệt theo thứ tự trước - preorder (Node-Left-Right: NLR)
Duyệt theo thứ tự giữa - inorder (Left-Node-Right: LNR)
Duyệt theo thứ tự sau - postorder (Left-Right-Node: LRN)
Tên của 3 kiểu duyệt này được đặt dựa trên trình tự của việc
thăm nút gốc so với việc thăm 2 cây con
20. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
Duyệt theo thứ tự trước NLR (Node-Left-Right)
Kiểu duyệt này trước tiên thăm nút gốc sau đó thăm các nút của
cây con trái rồi đến cây con phải
Thủ tục duyệt có thể trình bày đơn giản như sau:
20
void NLR (Node* t)
{
if (t != NULL)
{
// Xử lý t tương ứng theo nhu cầu
NLR(t->pLeft);
NLR(t->pRight);
}
}
21. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
NLR
21
A
B
D
H I
N
E
J K
O
C
F
L
P
G
M
AKết quả: B D H I N E J O K C F L P G M
22. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
Duyệt theo thứ tự giữa LNR (Left-Node-Right)
Kiểu duyệt này trước tiên thăm các nút của cây con trái sau đó
thăm nút gốc rồi đến cây con phải
Thủ tục duyệt có thể trình bày đơn giản như sau:
22
void LNR(Node* t)
{
if (t != NULL)
{
LNR(t->pLeft);
//Xử lý nút t theo nhu cầu
LNR(t->pRight);
}
}
23. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
LNR
23
A
B
D
H I
N
E
J K
O
C
F
L
P
G
M
HKết quả: D N I B J O E K A F P L C M G
24. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
Duyệt theo thứ tự giữa LRN (Left-Right-Node)
Kiểu duyệt này trước tiên thăm các nút của cây con trái sau đó
thăm đến cây con phải rồi cuối cùng mới thăm nút gốc
Thủ tục duyệt có thể trình bày đơn giản như sau:
24
void LRN(Node* t)
{
if (t != NULL)
{
LRN(t->pLeft);
LRN(t->pRight);
// Xử lý tương ứng t theo nhu cầu
}
}
25. Ch ng 5: Cây (Tree)ươ
Binary Tree - Duyệt cây nhị phân
LRN
25
A
B
D
H I
N
E
J K
O
C
F
L
P
G
M
HKết quả: N I D O J K E B P L F M G C A
26. Ch ng 5: Cây (Tree)ươ
Trắc nghiệm
The order in which the nodes of this tree would be visited by a
post-order traversal is
26
a) GCMBEJQDFKY
b) BCDFEJKYQMG
c) GCBEDFMJKQY
d) BDFECKJYQMG
27. Ch ng 5: Cây (Tree)ươ
Trắc nghiệm
The order in which the nodes of this tree would be visited by a
pre-order traversal is
27
a) GCMBEJQDFKY
b) BCDFEJKYQMG
c) BCDEFGKJMYQ
d) GCBEDFMJKQY
28. Ch ng 5: Cây (Tree)ươ
Trắc nghiệm
For the binary tree, preorder and inorder traversals are as
follows:
preorder: G C B E D F M J K Q Y
inorder: B C D E F G K J M Y Q
What the postorder for this tree?
28
G
C M
B E J Q
KFD Y
29. Ch ng 5: Cây (Tree)ươ
Một số thao tác trên cây
Đếm số node
Đếm số node lá
Tính chiều cao
...
29
30. Ch ng 5: Cây (Tree)ươ
Đếm số node
Thuật toán:
Nếu Tree rỗng, Số node (Tree) = 0
Ngược lại, Số node (Tree) = 1 + Số node (Tree.Left) + Số node
(Tree.Right)
30
31. Ch ng 5: Cây (Tree)ươ
Đếm số node lá
Thuật toán:
Nếu Tree rỗng, Số nút lá (Tree) = 0
Nếu Tree là nút lá, Số nút lá (Tree) = 1 + Số nút lá (Tree.Left) +
Số nút lá (Tree.Right)
Nếu Tree không là nút lá, Số nút lá (Tree) = Số nút lá
(Tree.Left) + Số nút lá (Tree.Right)
31
32. Ch ng 5: Cây (Tree)ươ
Tính chiều cao
Thuật toán:
Nếu Tree rỗng, Height(Tree) = 0
Ngược lại, Height(Tree) = 1 + max(Height(Tree.Left),
Height(Tree.Right))
32
34. Ch ng 5: Cây (Tree)ươ
Binary Search Tree - Định nghĩa
34
Là cây nhị phân
Giá trị của một node bất kỳ luôn lớn
hơn giá trị của tất cả các node bên
trái và nhỏ hơn giá trị tất cả các node
bên phải
Nút có giá trị nhỏ nhất nằm ở trái
nhất của cây
Nút có giá trị lớn nhất nằm ở phải
nhất của cây
7777
33 3636
11 66 1515 4040
232344
35. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Biểu diễn
35
Cấu trúc dữ liệu của CNPTK là cấu trúc dữ liệu biểu diễn cây
nhị phân
Thao tác duyệt cây trên CNPTK hoàn toàn giống như trên cây
nhị phân
Chú ý: khi duyệt theo thứ tự giữa, trình tự các nút duyệt qua sẽ
cho ta một dãy các nút theo thứ tự tăng dần của khóa
39. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Thêm
39
40401515446611363633
7 36 3 1 6 4 15 40
7777 Nếu node cần
thêm < node đang
xét thì thêm về
bên trái.
Ngược lại thì thêm
về bên phải.
40. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Thêm
Thêm một phần tử vào cây:
40
void InsertNode(Node *T, DataType X)
{
if (T == NULL)
{
T = new Node;
T->data = X
T->pLeft=T->pRight=NULL;
}
else
{
if(X > T->data)
InsertNode(T->pRight,X);
else if(X < T->data) InsertNode(T->pLeft,X);
}
}
41. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Tìm kiếm
41
25
10
3
1 6
5
18
12 20
13
37
29
35
32
50
41
Tìm kiếm 13
Khác nhauGiống nhauNode gốc nhỏ hơnNode gốc lớn hơn
Tìm thấy
Số node duyệt: 5
Tìm kiếm trên CNPTK
42. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Tìm kiếm
42
25
10
3
1 6
5
18
12 20
13
37
29
35
32
50
41
Tìm kiếm 14
Khác nhauNode gốc nhỏ hơnNode gốc lớn hơn
Không tìm thấy
Số node duyệt: 5
Tìm kiếm trên CNPTK
43. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Tìm kiếm
Tìm một phần tử x trong CNPTK (dùng đệ quy):
43
Node* searchNode(Node *T, DataType X)
{
if (T)
{
if(T->data == X)
return T;
if(T->data > X)
return searchNode(T->pLeft, X);
return searchNode(T->pRight, X);
}
return NULL;
}
44. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Tìm kiếm
Tìm một phần tử x trong CNPTK (dùng vòng lặp):
44
Node* searchNode(Node* T, DataType x)
{
Node *p = T;
while (p != NULL)
{
if(x == p->data) return p;
else if(x < p->data) p = p->pLeft;
else p = p->pRight;
}
return NULL;
}
45. Ch ng 5: Cây (Tree)ươ
Bài tập
Viết các hàm:
Đếm số nút trên cây: CountNode
Đếm số nút lá: CountLeaf
Đếm số nút trong: CountInnerNode
Xác định độ sâu/chiều cao của cây
Tìm giá trị nhỏ nhất/lớn nhất trên cây
Tính tổng các giá trị trên cây
Đếm số nút có giá trị bằng x
Xuất các số nguyên tố trên cây
45
46. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
46
Việc hủy một phần tử X ra khỏi cây phải bảo đảm điều kiện
ràng buộc của CNPTK
Có 3 trường hợp khi hủy nút X có thể xảy ra:
X là nút lá
X chỉ có 1 con (trái hoặc phải)
X có đủ cả 2 con
47. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
47
Trường hợp 1: X là nút lá
Chỉ đơn giản hủy X vì nó không móc nối đến phần tử nào khác
44
18 88
13 37 59 108
15 23 40 55 71
Hủy X=40
48. Ch ng 5: Cây (Tree)ươ
23
Binary Search Tree – Hủy một phần tử có
khóa X
Trường hợp 2: X chỉ có 1 con (trái hoặc phải)
Trước khi hủy X ta móc nối cha của X với con duy nhất của nó
48
44
18 88
13 37 59 108
15 23 55 71
Hủy X=37
49. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
Trường hợp 3: X có đủ 2 con:
Không thể hủy trực tiếp do X có đủ 2 con
Hủy gián tiếp:
Thay vì hủy X, ta sẽ tìm một phần tử thế mạng Y. Phần tử
này có tối đa một con
Thông tin lưu tại Y sẽ được chuyển lên lưu tại X
Sau đó, nút bị hủy thật sự sẽ là Y giống như 2 trường hợp
đầu
Vấn đề: chọn Y sao cho khi lưu Y vào vị trí của X, cây vẫn
là CNPTK
49
50. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
Cách chọn phần tử thế mạng:
Phần tử nhỏ nhất (trái nhất) trên cây con phải (của nút muốn
xóa)
Phần tử lớn nhất (phải nhất) trên cây con trái (của nút muốn
xóa)
Việc chọn lựa phần tử nào là phần tử thế mạng phụ thuộc vào
ý thích của người lập trình
Ở đây, ta sẽ chọn phần tử nhỏ nhất trên cây con phải làm phần
tử thế mạng
50
51. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
Trường hợp 3: X có đủ 2 con:
Khi hủy phần tử X=18 ra khỏi cây:
51
4444
8888
1313 3737 5959 108108
4040 5555 7171
Hủy X=18
3030
2323
1818
X
Chọn phần tử nhỏ nhất
trên cây con phải
phần tử 23 là phần
tử thế mạng
2323
52. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
52
Xóa 51
53. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
53
Xóa 83
54. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
54
Xóa 36
55. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
55
Xóa nút gốc:
56. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
56
Xóa nút gốc:
42 là thế mạng
57. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
57
Kết quả xóa:
58. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
58
Xóa gốc 42
59. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
Xóa gốc 42
45 thế mạng
59
60. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
60
Kết quả xóa:
61. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
Các hàm dùng để hủy 1 phần tử:
Hàm delNode trả về giá trị 1, 0 khi hủy thành công hoặc không
có X trong cây:
int delNode (Node* &T, DataType X)
Hàm searchStandFor tìm phần tử thế mạng q và gán dữ liệu của
q cho nút muốn xóa p
void searchStandFor(Node* &p, Tree &q)
61
62. Ch ng 5: Cây (Tree)ươ
62
Binary Search Tree – Hủy một phần tử có
khóa X
Hủy một nút
int delNode(Node* &T, DataType X)
{
if (T == NULL) return 0;
if (T->data > X) return delNode(T->pLeft, X);
if (T->data < X) return delNode(T->pRight, X);
Node *p = T;
if (T->pLeft == NULL) T = T->pRight;
else if (T->pRight == NULL) T = T->pLeft;
else // T có đủ 2 con
searchStandFor(p, T->pRight);
delete p;
}
63. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy một phần tử có
khóa X
Tìm phần tử thế mạng (nhỏ nhất trên cây con phải):
63
void searchStandFor(Node* &p, Node* &q)
{
if (q->pLeft != NULL)
searchStandFor (p, q->pLeft);
else
{
p->data = q->data;
p = q;
q = q->pRight;
}
}
64. Ch ng 5: Cây (Tree)ươ
Binary Search Tree – Hủy toàn bộ
cây
Việc toàn bộ cây có thể được thực hiện thông qua thao tác
duyệt cây theo thứ tự sau. Nghĩa là ta sẽ hủy cây con trái, cây
con phải rồi mới hủy nút gốc
64
void removeTree(Node* &T)
{
if (T)
{
removeTree(T->pLeft);
removeTree(T->pRight);
delete(T);
}
}
65. Ch ng 5: Cây (Tree)ươ
Bài tập
Viết các hàm:
Đếm số nút có đúng 1 con
Đếm số nút có đúng 2 con
Đếm số nguyên tố trên cây
Tính tổng các nút có đúng 1 con
Tính tổng các nút có đúng 2 con
Tính tổng các số chẵn
Nhập x, tìm giá trị nhỏ nhất trên cây mà lớn hơn x
Xuất số nguyên tố nhỏ nhất trên cây
Nhập x, tìm x trên cây, nếu tìm thấy x thì cho biết x có bao
nhiêu con
Xóa 1 nút
65
Editor's Notes
This is a tree because it is a set of nodes {A,B,C,D,E,F,G,H,I}, with node A as a root node and the
remaining nodes partitioned into three disjointed sets {B,G,H,I}, { C,E,F} and {D}, respectively. Each of
these sets is a tree because each satisfies the aforementioned definition properly.
Even though this is a set of nodes {A,B,C,D,E,F,G,H,I}, with node A as a root node, this is not a tree
because the fact that node E is shared makes it impossible to partition nodes B through I into
disjointed sets.
Height Number of nodes which must be traversed from the root to reach a leaf of a tree.
Level of a Node: We define the level of the node by taking the level of the root node as 1(???), and incrementing it by 1 as we move from the root towards the subtrees.
A full binary tree is a binary of depth k having 2^k − 1 nodes. If it has &lt; 2^k − 1, it is not a full binary tree.
An array representation of a binary tree is not suitable for frequent insertions and deletions, even
though no storage is wasted if the binary tree is a complete binary tree. It makes insertion and deletion
in a tree costly. Therefore, instead of using an array representation, we can use a linked
representation, in which every node is represented as a structure with three fields…
Tham khảo giáo trình trang 154: Constructing a Binary Tree Using the Preorder and Inorder Traversals
int Dem(Tree t)
{
if (t != NULL)
{
return 1+Dem(t-&gt;pLeft)+ Dem(t-&gt;pRight);
}
return 0;
}
int DemLa(Tree t)
{
if (t != NULL)
{
if (t-&gt;pLeft==NULL && t-&gt;pRight==NULL)
return 1+DemLa(t-&gt;pLeft)+ DemLa(t-&gt;pRight);
else
return DemLa(t-&gt;pLeft)+ DemLa(t-&gt;pRight);
}
return 0;
}
public static int height( BinaryTreeInterface t ){
// Post: Returns height of subtree.
if( t.isEmpty() )
return 0;
else {
int leftHt = height( t.getLeftSubtree() );
int rightHt = height( t.getRightSubtree() );
return 1 + Math.max( leftHt, rightHt );
}
}
public BinaryTreeInterface getLeftSubtree() {
// Pre: this is not the empty subtree.
// Post: returns the left subtree of this.
return this.leftChild;
}
int Height( Tree t ){
if( t )
{
int leftH = Height( t-&gt;pLeft );
int rightH = Height( t-&gt;pRight );
if (leftH &gt; rightH)
return 1 + leftH ;
else
return 1 + rightH;
}
return 0;
}
Trước khi huûy X ta moùc noái cha cuûa X vôùi con duy nhaát cuûa noù