CHƯƠNG 5
CẤU TRÚC DỮ LIỆU CÂY (TREE)
5.1- Định nghĩa và khỏi niệm
Cõy là một tập hợp hữu hạn cỏc node cú cựng chung một kiểu dữ liệu, trong đú cú
một node đặc biệt gọi là node gốc (root). Giữa cỏc node cú một quan hệ phõn cấp gọi là
“quan hệ cha con”. Cú thể định nghĩa một cỏch đệ qui về cõy như sau:
• Một node là một cõy. Node đú cũng là gốc (root) của cõy ấy.
• Nếu n là một node và T
1
, T
2
, . , T
k
là cỏc cõy với n
1
, n
2
, . . , n
k
lần lượt là gốc thỡ
một cõy mới T sẽ được tạo lập bằng cỏch cho node n trở thành cha của cỏc node n
1
,
n
2
, . . , n
k
hay node n trở thành gốc và T
1
, T
2
, . ., T
k
là cỏc cõy con (subtree) của gốc.
Vớ dụ: cấu trỳc tổ chức thư mục (directory) của dos là một cấu trỳc cõy.
Hỡnh 5.1- Vớ dụ về một cõy thư mục
Một cõy được gọi là rỗng nếu nú khụng cú bất kỳ một node nào. Số cỏc node con
của một node được gọi là cấp (degree) của node đú. Vớ dụ: trong cõy 5.2 sau, cấp của node
A là 3, cấp của node B là 2, cấp của node D là 3, cấp của node H là 2.
193
5
5.
1
5.
2
5.
3
5.
4
5.1.1 5.1.2 5.3.1 5.3.2 5.4.1 5.4.2
A
B C D
E F G H I
J K
Node cú cấp bằng 0 được gọi là lỏ (leaf) hay node tận cựng (terminal node). Vớ dụ:
cỏc node E, F, C, G, I, J, K được gọi là lỏ. Node khụng là lỏ được gọi là node trung gian
hay node nhỏnh (branch node). Vớ dụ node B, D, H là cỏc node nhỏnh.
Cấp cao nhất của node trờn cõy gọi là cấp của cõy, trong trường hợp cõy trong hỡnh
5.2 cấp của cõy là 3.
Gốc của cõy cú số mức là 1. Nếu node cha cú số mức là i thỡ node con cú số mức là
i+1. Vớ dụ gốc A cú số mức là 1, D cú số mức là 2, G cú số mức là 3, j cú số mức là 4.
Chiều cao (height) hay chiều sõu (depth) của một cõy là số mức lớn nhất của node
trờn cõy đú. Cõy 5.2 cú chiều cao là 4.
Đường đi từ node n
1
đến n
k
là dóy cỏc node n
1
, n
2
, . ., n
k
sao cho n
i
là node cha của
node n
i+1
(1<=i<k), độ dài của đường đi (path length) được tớnh bằng số cỏc node trờn
đường đi trừ đi 1 vỡ nú phải tớnh từ node bắt đầu và node kết thỳc. Vớ dụ: trong cõy 5.2
đường đi từ node A tới node G là 2, đường đi từ node A đến node K là 3.
Một cõy được gọi là cú thứ tự nếu chỳng ta xột đến thứ tự cỏc cõy con trong cõy
(ordered tree), ngược lại là cõy khụng cú thứ tự (unordered tree). Thụng thường cỏc cõy
con được tớnh theo thứ tự từ trỏi sang phải.
5.2- Cõy nhị phõn
Cõy nhị phõn là một dạng quan trọng của cấu trỳc cõy cú đặc điểm là mọi node trờn
cõy chỉ cú tối đa là hai node con. Cõy con bờn trỏi của cõy nhị phõn được gọi là left
subtree, cõy con bờn phải của cõy được gọi là right subtree. Đối với cõy nhị phõn, bao giờ
cũng được phõn biệt cõy con bờn trỏi và cõy con bờn phải. Như vậy, cõy nhị phõn là một
cõy cú thứ tự. Vớ dụ trong hỡnh 5.3 đều là cỏc cõy nhị phõn:
194
Hỡnh 5.3 cỏc cõy nhị phõn
Cỏc cõy nhị phõn cú dạng đặc biệt bao gồm:
• Cõy nhị phõn lệch trỏi (hỡnh 5.4a): là cõy nhị phõn chỉ cú cỏc node bờn trỏi.
• Cõy nhị phõn lệnh phải (hỡnh 5.4b): là cõy chỉ bao gồm cỏc node phải.
• Cõy nhị phõn zic zắc (hỡnh 5.4 c, 5.4d): node trỏi và node phải của cõy đan xen nhau
thành một hỡnh zic zắc.
• Cõy nhị phõn hoàn chỉnh ( strictly binary tree: hỡnh 5.4e) : Một cõy nhị phõn được gọi
là hoàn chỉnh nếu như node gốc và tất cả cỏc node trung gian đều cú hai con.
• Cõy nhị phõn đầy đủ (complete binary tree : hỡnh 5.4f): Một cõy nhị phõn được gọi là
đầy đủ với chiều sõu d thỡ nú phải là cõy nhị phõn hoàn chỉnh và tất cả cỏc node lỏ đều
cú chiều sõu là d.
195
A
B
C D
E
A
B
C D
E
A
B
C D
E
Hỡnh 5.4a Hỡnh 5.4b Hỡnh 5.4c Hỡnh 5.4d
Hỡnh 5.4 e Hỡnh 5.4f
• Cõy nhị phõn hoàn toàn cõn bằng (hỡnh 5.5): là cõy nhị phõn mà ở tất cả cỏc node
của nú số node trờn nhỏnh cõy con bờn trỏi và số node trờn nhỏnh cõy con bờn phải
chờnh lệnh nhau khụng quỏ 1. Nếu ta gọi N
l
là số node của nhỏnh cõy con bờn trỏi và
N
r
là số node của nhỏnh cõy con bờn phải, khi đú cõy nhị phõn hoàn toàn cõn bằng chỉ
cú thể ở một trong 3 trường hợp:
• Số node nhỏnh cõy con bờn trỏi bằng số node nhỏnh cõy con bờn phải bằng N
l
=
N
r
(hỡnh 5.5a).
• Số node nhỏnh cõy con bờn trỏi bằng số node nhỏnh cõy con bờn phải cộng 1 N
l
= N
r+1
(hỡnh 5.5b)
• Số node nhỏnh cõy con bờn trỏi bằng số node nhỏnh cõy con bờn phải trừ 1 N
l
= N
r-1
(hỡnh 5.5c).
196
A
B
C
D
E
A
B
C
D
E
A
B
C
D
E
A
B
C
D
E
A
B C
D E F G
H I
A
B
C
D E F G
Hỡnh 5.5a Bỡnh 5.5b Hỡnh 5.5c
• Cõy nhị phõn tỡm kiếm: là một cõy nhị phõn hoặc bị rỗng hoặc tất cả cỏc node trờn
cõy thỏa món điều kiện sau:
• Nội dung của tất cả cỏc node thuộc nhỏnh cõy con bờn trỏi đều nhỏ hơn nội dung
của node gốc.
• Nội dung của tất cả cỏc node thuộc nhỏnh cõy con bờn phải đều lớn hơn nội
dung của node gốc.
• Cõy con bờn trỏi và cõy con bờn phải cũng tự nhiờn hỡnh thành hai cõy nhị phõn
tỡm kiếm.
Hỡnh 5.6- vớ dụ về cõy nhị phõn tỡm kiếm
5.3- Biểu diễn cõy nhị phõn
5.3.1- Biểu diễn cõy nhị phõn bằng danh sỏch tuyến tớnh
Trong trường hợp cõy nhị phõn đầy đủ, ta cú thể dễ dàng biểu diễn cõy nhị phõn
bằng một mảng lưu trữ kế tiếp. Trong đú node gốc là phần tử đầu tiờn của mảng (phần tử
197
A
B C
D
E
A
B C
D E F
A
B C
D
E
F
2
0
1
2
3
0
8 1
5
2
5
3
7
6 1
0
2
2
2
8
4
0
1
0
9
8
6
1
0
1
9
2
9
3
9
thứ 1), node con thứ i>=1 của cõy nhị phõn là phần tử thứ 2i, 2i + 1 hay cha của node thứ j
là [j/2]. Với qui tắc đú, cõy nhị phõn cú thể biểu diễn bằng một vector V sao cho nội dung
của node thứ i được lưu trữ trong thành phần V[i] của vector V. Ngược lại, nếu biết địa chỉ
của phần tử thứ i trong vector V chỳng ta cũng hoàn toàn xỏc định được ngược lại địa chỉ
của node cha, địa chỉ node gốc trong cõy nhị phõn.
Vớ dụ: cõy nhị phõn trong hỡnh 5.7 sẽ được lưu trữ kế tiếp như sau:
V[0] V[1] V[2] V[3] V[4] V[5] V[6]
Hỡnh 5.7- Lưu trữ kế tiếp của cõy nhị phõn
Đối với cõy nhị phõn khụng đầy đủ, việc lưu trữ bằng mảng tỏ ra khụng hiệu quả vỡ
chỳng ta phải bỏ trống quỏ nhiều phần tử gõy lóng phớ bộ nhớ như trong vớ dụ sau:
Hỡnh 5.8- Lưu trữ kế tiếp của cõy nhị phõn khụng đầy đủ
V[0] V[1] V[2] V[3] V[4] V[5] V[6]
5.3.2- Biểu diễn cõy nhị phõn bằng danh sỏch múc nối
Trong cỏch lưu trữ cõy nhị phõn bằng danh sỏch múc nối, mỗi node được mụ tả
bằng ba loại thụng tin chớnh :
left là một con trỏ trỏ tới node bờn trỏi của cõy nhị phõn; infor : là thụng tin về node,
infor cú thể là một biến đơn hoặc một cấu trỳc; right là một con trỏ trỏ tới node bờn phải
của cõy nhị phõn. Trong trường hợp node là node lỏ thỡ con trỏ left và con trỏ right được
trỏ tới con trỏ NULL. Đối với node lệch trỏi, con trỏ right sẽ trỏ tới con trỏ NULL, ngược
198
3
0
2
5
3
7
2
2
2
8
4
0
3
5
30 25 37 22 28 35 40
3
0
2
5
3
7
2
2
3
5
30 25 37 22 φ 35 φ
lại đối với node lệch phải, con trỏ left cũng sẽ trỏ tới con trỏ NULL. Cấu trỳc của một node
được mụ tả trong hỡnh 5.9.
Hỡnh 5.9 mụ tả một node của cõy nhị phõn.
Vớ dụ: cõy nhị phõn trong hỡnh 5.10 sẽ được biểu diễn bằng danh sỏch liờn kết như
sau:
Hỡnh 5.10: biểu diễn cõy nhị phõn bằng danh sỏch múc nối .
5.4- Cỏc thao tỏc trờn cõy nhị phõn
5.4.1- Định nghĩa cõy nhị phõn bằng danh sỏch tuyến tớnh
Mỗi node trong cõy được khai bỏo như một cấu trỳc gồm 3 trường: infor, left, right.
Toàn bộ cõy cú thể coi như một mảng mà mỗi phần tử của nú là một node. Trường infor
tổng quỏt cú thể là một đối tượng dữ liệu kiểu cơ bản hoặc một cấu trỳc. Vớ dụ: định nghĩa
một cõy nhị phõn lưu trữ danh sỏch cỏc số nguyờn:
#define MAX 100
#define TRUE 1
#define FALSE 0
struct node {
int infor;
int left;
int right;
};
typedef structnode node[MAX];
199
Left Infor Right
3
0
2
5
3
7
2
2
3
5
Left 30 Right
Left 25 NULL Left 37 NULL
NULL 22 NULL NULL 35 NULL
5.4.2- Định nghĩa cõy nhị phõn theo danh sỏch liờn kết:
struct node {
int infor;
struct node *left;
struct node *right;
}
typedef struct node *NODEPTR
5.4.3- Cỏc thao tỏc trờn cõy nhị phõn
Cấp phỏt bộ nhớ cho một node mới của cõy nhị phõn:
NODEPTR Getnode(void) {
NODEPTR p;
p= (NODEPTR) malloc(sizeof(struct node));
return(p);
}
Giải phúng node đó được cấp phỏt
void Freenode( NODEPTR p){
free(p);
}
Khởi động cõy nhị phõn
void Initialize(NODEPTR *ptree){
*ptree=NULL;
}
Kiểm tra tớnh rỗng của cõy nhị phõn:
int Empty(NODEPTR *ptree){
if (*ptree==NULL)
200
return(TRUE);
return(FALSE);
}
Tạo một node lỏ cho cõy nhị phõn:
• Cấp phỏt bộ nhớ cho node;
• Gỏn giỏ trị thụng tin thớch hợp cho node;
• Tạo liờn kết cho node lỏ;
NODEPTR Makenode(int x){
NODEPTR p;
p= Getnode();// cấp phỏt bộ nhớ cho node
p ->infor = x; // gỏn giỏ trị thụng tin thớch hợp
p ->left = NULL; // tạo liờn kết trỏi của node lỏ
p ->right = NULL;// tạo liờn kết phải của node lỏ
return(p);
}
Tạo node con bờn trỏi của cõy nhị phõn:
Để tạo được node con bờn trỏi là node lỏ của node p, chỳng ta thực hiện như sau:
• Nếu node p khụng cú thực (p==NULL), ta khụng thể tạo được node con bờn trỏi của
node p;
• Nếu node p đó cú node con bờn trỏi (p->left!=NULL), thỡ chỳng ta cũng khụng thể
tạo được node con bờn trỏi node p;
• Nếu node p chưa cú node con bờn trỏi, thỡ việc tạo node con bờn trỏi chớnh là thao
tỏc make node đó được xõy dựng như trờn;
Hỡnh 5.11 sẽ minh họa cho thao tỏc tạo node con X phớa bờn trỏi của node D.
void Setleft(NODEPTR p, int x ){
if (p==NULL){ // nếu node p khụng cú thực thỡ khụng thể thực hiện được
printf(“\n Node p khụng cú thực”);
201
delay(2000); return;
}
// nếu node p cú thực và tồn tại lỏ con bờn trỏi thỡ cũng khụng thực hiện được
else if ( p ->left !=NULL){
printf(“\n Node p đó cú node con bờn trỏi”);
delay(2000); return;
}
// nếu node cú thực và chưa cú node trỏi
else
p ->left = Makenode(x);
}
Hỡnh 5.11 mụ tả thao tỏc thờm node con bờn trỏi cõy nhị phõn
Tạo node con bờn phải của cõy nhị phõn:
Để tạo được node con bờn phải là node lỏ của node p, chỳng ta làm như sau:
• Nếu node p khụng cú thực (p==NULL), thỡ ta khụng thể thực hiện được thao tỏc
thờm node lỏ vào node phải node p;
• Nếu node p cú thực (p!=NULL) và đó cú node con bờn phải thỡ thao tỏc cũng
khụng thể thực hiện được;
• Nếu node p cú thực và chưa cú node con bờn phải thỡ việc tạo node con bờn phải
node p được thực hiện thụng qua thao tỏc Makenode();
202
A
B C
E FD
H IG
A
B
C
E FD
H IG
X
Hỡnh 5.12 sẽ minh họa cho thao tỏc tạo node con X phớa bờn phải của node E.
void Setright(NODEPTR p, int x ){
if (p==NULL){ // Nếu node p khụng cú thực
printf(“\n Node p khụng cú thực”);
delay(2000); return;
}
// Nếu node p cú thực & đó cú node con bờn phải
else if ( p ->right !=NULL){
printf(“\n Node p đó cú node con bờn phải”);
delay(2000); return;
}
// Nếu node p cú thực & chưa cú node con bờn phải
else
p ->right = Makenode(x);
}
Hỡnh 5.12 mụ tả thao tỏc thờm node con bờn phải cõy nhị phõn
Thao tỏc xoỏ node con bờn trỏi cõy nhị phõn
Thao tỏc loại bỏ node con bờn trỏi node p được thực hiện như sau:
• Nếu node p khụng cú thực thỡ thao tỏc khụng thể thực hiện;
203
A
B C
E FD
IG
A
B
C
E FD
X IG
• Nếu node p cú thực (p==NULL) thỡ kiểm tra xem p cú node lỏ bờn trỏi hay
khụng;
+ Nếu node p cú thực và p khụng cú node lỏ bờn trỏi thỡ thao tỏc cũng khụng thể
thực hiện được;
+ Nếu node p cú thực (p!=NULL) và cú node con bờn trỏi là q thỡ:
- Nếu node q khụng phải là node lỏ thỡ thao tỏc cũng khụng thể thực hiện
được (q->left!=NULL || q->right!=NULL);
- Nếu node q là node lỏ (q->left==NULL && q->right==NULL) thỡ:
• Giải phúng node q;
• Thiết lập liờn kết mới cho node p;
Thuật toỏn được thể hiện bằng thao tỏc Delleft() như dưới đõy:
int Delleft(NODEPTR p) {
NODEPTR q; int x;
if ( p==NULL)
printf(“\n Node p khụng cú thực”);delay(2000);
exit(0);
}
q = p ->left; // q là node cần xoỏ;
x = q->infor; //x là nội dung cần xoỏ
if (q ==NULL){ // kiểm tra p cú lỏ bờn trỏi hay khụng
printf(“\n Node p khụng cú lỏ bờn trỏi”);
delay(2000); exit(0);
}
if (q->left!=NULL || q->right!=NULL) {
// kiểm tra q cú phải là node lỏ hay khụng
printf(“\n q khụng là node lỏ”);
204
delay(2000); exit(0);
}
p ->left =NULL; // tạo liờn kết mới cho p
Freenode(q); // giải phúng q
return(x);
}
Thao tỏc xoỏ node con bờn phải cõy nhị phõn
Thao tỏc loại bỏ node con bờn phải node p được thực hiện như sau:
• Nếu node p khụng cú thực thỡ thao tỏc khụng thể thực hiện;
• Nếu node p cú thực (p==NULL) thỡ kiểm tra xem p cú node lỏ bờn phải hay
khụng;
+ Nếu node p cú thực và p khụng cú node lỏ bờn phải thỡ thao tỏc cũng khụng
thể thực hiện được;
+ Nếu node p cú thực (p!=NULL) và cú node con bờn phải là q thỡ:
- Nếu node q khụng phải là node lỏ thỡ thao tỏc cũng khụng thể thực hiện
được (q->left!=NULL || q->right!=NULL);
- Nếu node q là node lỏ (q->left==NULL && q->right==NULL) thỡ:
• Giải phúng node q;
• Thiết lập liờn kết mới cho node p;
Thuật toỏn được thể hiện bằng thao tỏc Delright() như dưới đõy:
int Delright(NODEPTR p) {
NODEPTR q; int x;
if ( p==NULL)
printf(“\n Node p khụng cú thực”);delay(2000);
exit(0);
}
205
q = p ->right; // q là node cần xoỏ;
x = q->infor; //x là nội dung cần xoỏ
if (q ==NULL){ // kiểm tra p cú lỏ bờn phải hay khụng
printf(“\n Node p khụng cú lỏ bờn phải”);
delay(2000); exit(0);
}
if (q->left!=NULL || q->right!=NULL) {
// kiểm tra q cú phải là node lỏ hay khụng
printf(“\n q khụng là node lỏ”);
delay(2000); exit(0);
}
p ->right =NULL; // tạo liờn kết cho p
Freenode(q); // giải phúng q
return(x);
}
Thao tỏc tỡm node cú nội dung là x trờn cõy nhị phõn:
Để tỡm node cú nội dung là x trờn cõy nhị phõn, chỳng ta cú thể xõy dựng bằng thủ
tục đệ qui như sau:
• Nếu node gốc (proot) cú nội dung là x thỡ proot chớnh là node cần tỡm;
• Nếu proot =NULL thỡ khụng cú node nào trong cõy cú nội dung là x;
• Nếu nội dung node gốc khỏc x (proot->infor!=x) và proot!=NULL thỡ:
• Tỡm node theo nhỏnh cõy con bờn trỏi (proot = proot->left);
• Tỡm theo nhỏnh cõy con bờn phải;
Thuật toỏn tỡm một node cú nội dung là x trong cõy nhị phõn được thể hiện như sau:
NODEPTR Search( NODEPTR proot, int x) {
206
NODEPTR p;
if ( proot ->infor ==x) // điều kiện dừng
return(proot);
if (proot ==NULL)
return(NULL);
p = Search(proot->left, x); // tỡm trong nhỏnh con bờn trỏi
if (p ==NULL) // Tỡm trong nhỏnh con bờn phải
Search(proot->right, x);
return(p);
}
5.5- Ba phộp duyệt cõy nhị phõn (Traversing Binary Tree)
Phộp duyệt cõy là phương phỏp thăm (visit) cỏc node một cỏch cú hệ thống sao cho
mỗi node chỉ được thăm một lần. Cú ba phương phỏp để duyệt cõy nhị phõn đú là :
• Duyệt theo thứ tự trước (Preorder Travesal);
• Duyệt theo thứ tự giữa (Inorder Travesal);
• Duyệt theo thứ tự sau (Postorder Travesal).
Hỡnh 5.13 mụ tả phương phỏp duyệt cõy nhị phõn
5.5.1- Duyệt theo thứ tự trước (Preorder Travesal)
• Nếu cõy rỗng thỡ khụng làm gỡ;
• Nếu cõy khụng rỗng thỡ :
207
A
B C
D E F G
+ Thăm node gốc của cõy;
+ Duyệt cõy con bờn trỏi theo thứ tự trước;
+ Duyệt cõy con bờn phải theo thứ tự trước;
Vớ dụ : với cõy trong hỡnh 5.13 thỡ phộp duyệt Preorder cho ta kết quả duyệt theo
thứ tự cỏc node là :A -> B -> D -> E -> C -> F -> G.
Với cỏch duyệt theo thứ tự trước, chỳng ta cú thể cài đặt cho cõy được định nghĩa
trong mục 5.4 bằng một thủ tục đệ qui như sau:
void Pretravese ( NODEPTR proot ) {
if ( proot !=NULL) { // nếu cõy khụng rỗng
printf(“%d”, proot->infor); // duyệt node gốc
Pretravese(proot ->left); // duyệt nhỏnh cõy con bờn trỏi
Pretravese(proot ->right); // Duyệt nhỏnh con bờn phải
}
}
5.5.2- Duyệt theo thứ tự giữa (Inorder Travesal)
• Nếu cõy rỗng thỡ khụng làm gỡ;
• Nếu cõy khụng rỗng thỡ :
+ Duyệt cõy con bờn trỏi theo thứ tự giữa;
+ Thăm node gốc của cõy;
+ Duyệt cõy con bờn phải theo thứ tự giữa;
Vớ dụ : cõy trong hỡnh 5.13 thỡ phộp duyệt Inorder cho ta kết quả duyệt theo thứ tự
cỏc node là :D -> B -> E -> A -> F -> C -> G.
Với cỏch duyệt theo thứ tự giữa, chỳng ta cú thể cài đặt cho cõy được định nghĩa
trong mục 5.4 bằng một thủ tục đệ qui như sau:
void Intravese ( NODEPTR proot ) {
if ( proot !=NULL) { // nếu cõy khụng rỗng
208
Intravese(proot ->left); // duyệt nhỏnh cõy con bờn trỏi
printf(“%d”, proot->infor); // duyệt node gốc
Intravese(proot ->right); // Duyệt nhỏnh con bờn phải
}
}
5.5.3- Duyệt theo thứ tự sau (Postorder Travesal)
• Nếu cõy rỗng thỡ khụng làm gỡ;
• Nếu cõy khụng rỗng thỡ :
+ Duyệt cõy con bờn trỏi theo thứ tự sau;
+ Duyệt cõy con bờn phải theo thứ tự sau;
+ Thăm node gốc của cõy;
Vớ dụ: cõy trong hỡnh 5.13 thỡ phộp duyệt Postorder cho ta kết quả duyệt theo thứ
tự cỏc node là :D -> E -> B -> F -> G-> C -> A .
Với cỏch duyệt theo thứ tự giữa, chỳng ta cú thể cài đặt cho cõy được định nghĩa
trong mục 5.4 bằng một thủ tục đệ qui như sau:
void Posttravese ( NODEPTR proot ) {
if ( proot !=NULL) { // nếu cõy khụng rỗng
Posttravese(proot ->left); // duyệt nhỏnh cõy con bờn trỏi
Posttravese(proot ->right); // duyệt nhỏnh con bờn phải
printf(“%d”, proot->infor); // duyệt node gốc
}
}
5.6- Cài đặt cõy nhị phõn bằng danh sỏch tuyến tớnh
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
209
#include <alloc.h>
#include <string.h>
#include <dos.h>
#define TRUE 1
#define FALSE 0
#define MAX 100
typedef struct{
int infor;
int left;
int right;
} nodetype;
nodetype node[MAX]; int avail;
int Getnode(void) {
int p;
if (avail==-1){
printf("\n Het node danh san");
return(avail);
}
p=avail;
avail=node[avail].left;// Gan gia tri NULL cho node la be trai. Vi chung ta dang
duyet cay theo Phuong phap tuyen tinh nen se la cay nhi phan day du va duyet ben trai
truoc ;
return(p);
}
void Freenode (int p){
node[p].left=avail;
avail=p;
}
void Initialize(int *ptree){
*ptree=-1;
}
210
int Empty(int *ptree){
if(*ptree==-1)
return(TRUE);
return(FALSE);
}
int Makenode(int x){
int p;
p=Getnode(); // ket qua cua cau lenh nay la no se tra lai cho p gia tri cua avail
Gia tri cua p trong bai la gia tri 0 vi ta co tai ham main avail = 0;
/*int Getnode(void) {
int p;
if (avail= =-1){
printf("\n Het node danh san");
return(avail);
}
p=avail;
avail=node[avail].left;// Gan gia tri NULL cho node la be trai. Vi chung ta dang
duyet cay theo Phuong phap tuyen tinh nen se la cay nhi phan day du va duyet ben trai
truoc ;
return(p);
}
*/
node[p].infor = x;// gan gia tri x la noi dung cho node dau tien hay chinh la node goc
node[p].left=-1;
node[p].right=-1;
return(p);// p o day la thu tu ma node cua node goc luc nay noi dung cua node goc co
gia tri la x.
}
void Setleft(int p, int x){
if (p==-1)
printf("\n Node p Khong co thuc");
else {
211
if (node[p].left!=-1)
printf("\n Nut p da co ben trai");
else
node[p].left=Makenode(x);
}
delay(2000);
}
void Setright(int p, int x){
if (p==-1)
printf("\n Node p Khong co thuc");
else {
if (node[p].right!=-1)
printf("\n Nut p da co ben phai");
else
node[p].right=Makenode(x);
}
delay(2000);
}
int Delleft(int p){
int q, x;
if (p ==-1){
printf("\n Node p khong co thuc");
delay(2000);return(-1);
}
else {
q=node[p].left;
x=node[q].infor;
if (q==-1){
printf("\n Node p khong co nut trai");
delay(2000);return(q);
}
212
else if (node[q].left!=-1 || node[q].right!=-1){
printf("\n Q khong la node la");
delay(2000); return(-1);
}
}
node[p].left=-1;
Freenode(q);return(x);
}
int Delright (int p){
int q, x;
if (p ==-1){
printf("\n Node p khong co thuc");
delay(2000);return(-1);
}
else {
q=node[p].right;
x=node[q].infor;
if (q==-1){
printf("\n Node p khong co nut trai");
delay(2000);return(q);
}
else if (node[q].left!=-1 || node[q].right!=-1){
printf("\n Q khong la node la");
delay(2000); return(-1);
}
}
node[p].right=-1;
Freenode(q);return(x);
}
void Pretrav(int proot){
if (proot!=-1){
213
printf("%5d",node[proot].infor);
Pretrav(node[proot].left);
Pretrav(node[proot].right);
}
}
void Intrav(int proot){
if (proot!=-1){
Intrav(node[proot].left);
printf("\n %d", node[proot].left);
Intrav(node[proot].right);
}
}
void Postrav(int proot){
if (proot!=-1){
Postrav(node[proot].left);
Postrav(node[proot].right);
printf("\n %d", node[proot].left);
}
}
int Search(int proot, int x){
int p;
if(node[proot].infor==x) // dieu kien dung
return(proot);
if(proot==-1) // Khong co nut co noi dung la x
return(-1);
p= Search(node[proot].left,x);// tim kiem cac cay con ben trai
if(p==-1)
p= Search(node[proot].right,x);// tim kiem cac cay con ben phai
return(p);
}
214
void Cleartree(int proot){
if (proot!=-1){
Cleartree(node[proot].left);
Cleartree(node[proot].right);
Freenode(proot);
}
}
void main(void ){
int i, noidung, noidung1,chucnang, p, ptree;
char c; clrscr();avail=0;
for (i=0; i<MAX; i++)
node[i].left=i+1;
node[MAX-1].left=-1;
Initialize(&ptree);
do {
clrscr();
printf("\n CAY NHI PHAN");
printf("\n 1- Tao node goc cua cay");
printf("\n 2- Them mot nut la ben trai");
printf("\n 3- Them mot nut la ben phai");
printf("\n 4- Xoa mot nut la ben trai");
printf("\n 5- Xoa mot nut la ben phai");
printf("\n 6- Duyet cay theo thu tu truoc");
printf("\n 7- Duyet cay theo thu tu giua");
printf("\n 8- Duyet cay theo thu tu sau");
printf("\n 9- Tim kiem tren cay");
printf("\n10- Xoa toan bo cay");
printf("\n 0- Ket thuc chuong trinh");
printf("\n Chuc nang lua chon:");scanf("%d", &chucnang);
switch(chucnang){
case 1:
215
if (!Empty(&ptree))
printf("\n Cay da co node goc");
else {
printf("\n Noi dung node goc:");
scanf("%d",&noidung);
ptree = Makenode(noidung);// ptree chinh la noid dung
cua node goc
}
delay(1000); break;
case 2:
if (Empty(&ptree))
printf("\n Cay chua co goc");
else {
printf("\n Node la can them:");
scanf("%d",&noidung);
p= Search(ptree,noidung);// muc dich cua ham nay o day
la de xem nut them vao co trung voi node goc khong
/*int Search(int proot, int x){
int p;
if(node[proot].infor==x) // dieu kien dung
return(proot);// o day p se khac -1;
if(proot==-1) // ton tai cay rong
return(-1);
p= Search(node[proot].left,x);
if(p==-1)
p= Search(node[proot].right,x);
return(p);
}
*/ if(p!=-1)
printf("\n Noi dung bi trung");
216
else {
printf("\n Noi dung node cha:");
scanf("%d",&noidung1);
p=Search(ptree,noidung1);// muc dich cua ham
nay la xem no co thoa man dieu kien la node co cay ben trai khong
if(p==-1)
printf("\n Khong thay node cha");
else
Setleft(p,noidung);
/* void Setleft(int p, int x){
if (p==-1)
printf("\n Node p Khong co thuc");
else {
if (node[p].left!=-1)
printf("\n Nut p da co ben trai");
else
node[p].left=Makenode(x);
}
delay(2000);
}*/
}
}
delay(2000);break;
case 3:
if (Empty(&ptree))
printf("\n Cay chua co goc");
else {
217