Cây (tree) - HOA CUONG

Download Report

Transcript Cây (tree) - HOA CUONG

Cây (tree)
1
Cây
• Định nghĩa:
• Rỗng là 1 cây
• T1 , T2 , ……Tm là cây thì
T
………
T1
T2
Tm
là cây (m-phân)
• T1 , T2 , ……Tm gọi là cây con của cây T
2
• Ví dụ
– Cây thư mục trong hệ điều hành
– Cây gia phả của 1 dòng họ
– Cây biểu thức: a-b+c*d/e
+
/
-
a
b
c
*
e
d
3
Một số khái niệm
mức 1
Q
w
T
•
•
•
•
•
•
E
Y
mức 2
R
U
P
O
A
R là cha của U, O, P
U, O, P là con của R
Q, R là đỉnh trước của A.
A là đỉnh sau của Q, R
T, Y, U, O, A là các nút lá (không có con)
Q là nút gốc
mức 3
mức 4
4
Phép duyệt
• Phép duyệt: Đưa ra tất cả các nút theo
một thứ tự nào đó, mỗi nút 1 lần
– Ví dụ: Tìm kiếm một file hay một folder trong
cây thư mục
• Phổ biến:
– Duyệt theo bề sâu (Depth First Search - DFS)
– Duyệt theo bề rộng (Breadth First Search BFS)
5
Cây nhị phân (Binary tree)
• Là cây bao gồm các nút, mỗi nút có tối đa 2 cây
con:
– Cây con bên trái (Left)
– Cây con bên phải (Right)
• Mô tả cấu trúc:
typedef struct nodet {
elem data;
struct nodet *left, *right;
} node;
typedef node *tree;
6
Duyệt theo bề sâu (DFS)
• Duyệt tiền tự (Preorder Search – NLR)
– Thăm nút gốc (Node)
– Duyệt cây con bên trái (Left)
– Duyệt cây con bên phải (Right)
• Duyệt trung tự (Inorder Search – LNR)
(tương tự)
• Duyệt hậu tự (Postorder Search – LRN)
(tương tự)
7
Duyệt theo bề sâu (DFS)
Q
w
T
R
Y
U
P
A
• NLR: QWTYRUPA
• LNR: TWYQURAP
• LRN: TYWUAPRQ
8
Ví dụ: Đếm số nút trên cây
int sonut(tree t)
{
if (t==NULL) return 0;
return 1 + sonut(t->left) + sonut(t->right);
}
9
Độ cao cây?
int h(tree t)
{
if (t==NULL) return 0;
return 1 + max(h(t->left), h(t->right));
}
Lưu ý: Viết hàm max, không dùng macro
10
Nhập cây số nguyên>0
void nhap(tree &t)
{
int x;
cin>>x;
if (x>0)
{
t = new node;
t->data = x;
nhap(t->left);
nhap(t->right);
}
else
t = NULL;
}
7
2
4
6
5
3
9
8
• Với cây trên dữ liệu nhập:
7 2 4 0 0 5 0 0 6
3 0 0 9 8 0 0 0
11
In cây (số nguyên)
void incay(tree t, int m=1)
{
if (t!=NULL)
{
incay(t->left, m+1);
cout<<endl<<setw(4*m)<<t->data;
incay(t->right, m+1);
}
}
12
Xóa toàn bộ cây
void xoacay(tree &t)
{
if (t!=NULL)
{
xoacay(t->left);
xoacay(t->right);
delete t;
t = NULL;
}
}
13
Đếm số nút lá trên cây
int
{
sonutla(tree t)
if (t==NULL) return 0;
if (t->left==NULL && t->right==NULL)
return 1;
return sonutla(t->left)+sonutla(t->right);
}
14
Đếm số nút có 1 con trên cây
int
{
sonut1(tree t)
if (t==NULL) return 0;
if ((t->left!=NULL) &&( t->right==NULL))
return 1+sonut1(t->left);
if ((t->left==NULL) &&( t->right!=NULL))
return 1+sonut1(t->right);
return sonut1(t->left)+sonut1(t->right);
}
15
Đếm số nút có 1 con trên cây
int sonut1(tree t)
{
if (t==NULL) return 0;
if ((t->left!=NULL && t->right==NULL)||
(t->left==NULL && t->right!=NULL))
return 1+sonut1(t->left) +sonut1(t->right);
return sonut1(t->left)+sonut1(t->right);
}
16
Đếm số nút có 1 con trên cây
int
{
sonut1(tree t)
if (t==NULL) return 0;
if ((t->left==NULL) ^( t->right==NULL))
return 1+sonut1(t->left)+sonut1(t->right);
return sonut1(t->left)+sonut1(t->right);
}
17
Đếm số nút trong trên cây (không
phải là lá)
int
{
sonuttrg(tree t)
if (t==NULL) return 0;
if (t->left==NULL && t->right==NULL)
return 0;
return 1+sonuttrg(t->left)+sonuttrg(t->right);
}
18
Tính trung bình cộng
void NLR(tree t, int &sn, int &tg)
{
float tbc(tree t)
if (t!=NULL)
{
{
tg+=t->data;
int sn, tg;
sn++;
sn = tg = 0;
NLR(t->left, sn, tg);
NLR(t, sn, tg);
NLR(t->right, sn, tg);
if (sn == 0) return 0.;
}
return float(tg)/sn;
}
}
19
Bài tập
•
•
•
•
•
Đếm số nút trên mức thứ k
Đếm số nút trên mức >= k
Đếm số nút trên mức <= k
Đếm số nút trên mức lẻ của cây
Tính trung bình cộng các nút ….
20
Đếm số nút trên mức lẻ của cây
int muccl(tree t, int m=1)
{
if (t==NULL) return 0;
return m%2 +muccl(t->left, m+1) +
muccl(t->right, m+1);
}
Gọi hàm:
cout<<“Số nút trên mức lẻ =“<<muccl(t);
cout<<“Số nút trên mức chẵn =“<<muccl(t, 0);
21
Đếm số nút trên mức thứ k
int nutk(tree t, int k)
{
if (t==NULL) return 0;
if (k>1)
return nutk(t->left,k-1)+nutk(t->right, k-1);
return 1;
}
22
void nlr(tree t, int k, int &sn, int &tg)
{
if (t!=NULL)
{
if (k>1)
{
nlr(t->left, k-1, sn, tg);
nlr(t->right, k-1, sn, tg);
}
else
{
sn++;
tg +=t->data);
float tbc(tree t, int k)
{
int sn, tg;
sn = tg = 0;
nlr(t, k, sn, tg);
if (sn == 0) return 0.;
return float(tg)/sn;
}
}
}
}
23
void nlr(tree t, int k, int &sn, int &tg)
{
if (t!=NULL)
{
sn++;
tg +=t->data);
if (k>1)
{
nlr(t->left, k-1, sn, tg);
nlr(t->right, k-1, sn, tg);
}
}
}
float tbc(tree t, int
k)
{
int sn, tg;
sn = tg = 0;
nlr(t, k, sn, tg);
if (sn == 0) return
0.;
return
float(tg)/sn;
24
}
void nlr(tree t, int k, int &sn, int
&tg)
{
if (t!=NULL)
{
if (k<=1)
{
sn++;
tg +=t->data);
}
nlr(t->left, k-1, sn, tg);
nlr(t->right, k-1, sn,
tg);
}
}
float tbc(tree t, int
k)
{
int sn, tg;
sn = tg = 0;
nlr(t, k, sn, tg);
if (sn == 0) return
0.;
return
float(tg)/sn;
25
}
Duyệt theo bề rộng (BFS)
Q
• Hay còn gọi là duyệt theo mức
• Sử dụng hàng đợi: Tuần tự từ
mức thấp đến cao, từ trái qua
phải
• Với cây trên ta có thứ tự các
nút được duyệt lần lượt:
Q W R T Y U P A
w
T
R
Y
U
P
A
SV cài đặt như bài tập
26
1. Khởi tạo hàng đợi q rỗng
2. M=0; p=NULL;
3. Nếu t!=NULL
•
•
Đưa p vào q
Đưa t vào q
4. Khi q!=rỗng
•
•
Lấy p ra khỏi q
Nếu p==NULL

Nếu q!=rỗng
•
•
•
•
M = M+1;
Đưa p vào q
//Xử lý
Ngược lại



Xử lý p->data
Nếu p->left != NULL thì Đưa p->left vào q
Nếu p->right != NULL thì Đưa p->right vào q
27
Cây tìm kiếm nhị phân
(Binary Search Tree – BST)
• Ý nghĩa: Phục vụ tìm kiếm nhị phân trên cấu
trúc động
• Định nghĩa: Là cây nhị phân thỏa điều kiện mọi
nút đều có khóa
– Lớn hơn khóa tất cả các nút trên cây con bên trái
– Nhỏ hơn khóa tất cả các nút trên cây con bên phải
28
BST
7
11
3
1
5
15
9
13
29
Chèn phần tử vào BST
• Thêm 12
12<
20
12
12>
5
30
10
12< 15
N
N
40
25
35
•
30
Chèn phần tử vào BST
void inserttree(tree &t, elem x)
{
if (t==NULL)
{
t = new node;
t->data = x;
t->left = t->right = NULL; 12>
}
else
5
if (x<t->data)
inserttree(t->left, x);
else
if (x>t->data)
inserttree(t->right, x);
}
12
N
12<
N
20
30
10
12< 15
40
25
35
31
Nhập cây (số nguyên >0)
t = NULL;
do {
cin>>x;
if (x>0)
inserttree(t, x);
} while (x>0);
32
Xóa phần tử trên BST
• Xóa 15
15<
15>
5
20
30
10
15= 15
12
40
25
35
33
• Xóa 20
20
30
10
5
15
12
40
25
35
34
void deletetree(tree &t, elem x)
{
t
if (t!=NULL)
15< 20
if (x<t->data)
t
deletetree(t->left, x);
else
if (x>t->data)
15> 10
deletetree(t->right, x);
t
else
{
15=
25
5
15
tree q = t;
if (t->right==NULL)
t = t->left;
12
else
if (t->left==NULL)
t = t->right;
else
del(t->left, q);
//tìm phtử thay thế
delete q;
}
}
30
40
35
35
Tìm phần tử thay thế (phần tử lớn nhất)
void del(tree &r, tree &q)
{
if (r->right!=NULL)
del(r->right, q);
else
{
q->data = r->data;
q = r;
r = r->left;
}
}
q
t
20
r
30
10
r
5
15
12
40
25
35
36
Tìm kiếm phần tử trên BST
Sinh viên cài đặt như bài tập
37
Tìm kiếm phần tử x
tree searchtree(tree t, elem x)
{
if (t==NULL)
return NULL;
if (x<t->data)
return searchtree(t->left, x);
if (x>t->data)
return searchtree (t->right, x);
return t;
}
38
Cắt cây có gốc x
tree cuttree(tree &t, elem x)
{
if (t==NULL)
return NULL;
if (x<t->data)
return cuttree (t->left, x);
if (x>t->data)
return cuttree (t->right, x);
{
tree q = t;
t = NULL;
return q;
}
}
39
void ink(tree t, int &k)
{
if (t!=NULL)
{
if (k>0)
ink(t->right, k);
if (k>0)
{
cout<<setw(4)<<t->data;
k--;
}
if (k>0)
ink(t->left, k);
}
}
40
CÂY CÂN BẰNG AVL
41
• Đưa vào cây:1, 2, 3, …, n
1
2
3
n
42
Định nghĩa
• Cây cân bằng hoàn toàn: Là cây nhị phân
thỏa điều kiện: Tất cả các nút đều có 2 cây
con chênh lệch về số nút tối đa 1
43
Định nghĩa
• Cây cân bằng AVL: Là cây nhị phân thỏa
điều kiện: Tất cả các nút đều có 2 cây con
chênh lệch về độ cao tối đa 1
44
Biểu diễn cây
p
45
Tổ chức cấu trúc dữ liệu
• Tương tự như BST, tuy nhiên mỗi node cần bổ
sung một vùng bal (balance), ghi nhận trạng
thái cân bằng tại node đó:
46
Mô tả cấu trúc dữ liệu
typedef struct nodet {
elem data;
struct nodet *left, *right;
int bal;
} node;
typedef node *AVLtree;
47
Trạng thái cân bằng tại 1 node
• Tại p có 3 trường hợp:
p
p->bal = 1
p
p->bal = 0
p
p->bal = -1
48
a) Trường hợp p->bal = 1
• Thêm vào làm cho hR tăng
hoặc xóa làm cho hL giảm
p1
p
p1
Đặt p1 = p->right
phép quay:
p->right=p1->left
p1->left =p
p
49
a1) Trường hợp p1->bal = 1
• Thêm vào làm cho hR tăng
hoặc xóa làm cho hL giảm
p1
p
p1
p
phép quay đơn:
p->right=p1->left
p1->left =p
p->bal =0
p1->bal=0
p = p1
50
a2) Trường hợp p1->bal = 0
• Chỉ xảy ra khi xóa làm cho hL giảm
p1
p
p1
phép quay đơn:
p->right=p1->left
p1->left =p
p->bal =1
p1->bal=-1
p = p1
p
51
a3) Trường hợp p1->bal = -1
• Không sử dụng phép quay đơn được vì sẽ mất cân
bằng
p1
p
p
p1
phép quay đơn:
p->right=p1->left
p1->left =p
52
a3) Trường hợp p1->bal = -1
• Đặt p2 = p1->left
p
Sử dụng phép quay kép
p->right=p2->left
p1
p2->left=p
p1->left=p2->right
p2->right=p1
p2
p
p1
p2
p->bal=(p2->bal==1? -1: 0)
p1->bal=(p2->bal==-1? 1: 0)
p2->bal = 0
p = p2
53
b) Trường hợp p->bal = -1
• Trường hợp này đối xứng với trường hợp
a), chính vì vậy ta chỉ cần thay
– left thành right
– 1 thành -1
và ngược lại
và ngược lại
54
Chèn phần tử vào AVL
• Tương tự như BST, tuy nhiên sau khi chèn
cây sẽ tăng độ cao, sử dụng biến h để ghi
nhận cây có tăng độ cao hay không? Nếu
cây con tăng độ cao thì phải xem lại thế
cân bằng tại nút đó.
55
Ví dụ minh họa
• Thêm lần lượt 3, 5 và 7
3
3
1
1
5
0
Thêm 7
5
3
7
0
Mất cân bằng tại 3
1
5
Sử dụng quay đơn
5
0
…và cân bằng
1
0
7
0
3
7
0
56
Ví dụ minh họa
Thêm 12, 10
Mất cân bằng tại 7
5
0
3
5
1
7
1
0
3
Và quay kép..
1
7
1
12 0
12 -1
Thêm 10
10
0
10
0
57
Ví dụ minh họa
• Thêm 11
5
0
10
1
10
3
0
7
0/1
12 0/-1
11
5
0
3
12
0
0
7
0
11
0
0
58
-1
• Thêm
8, 9 ?
10
5
0
3
12
0
0
7
0
11
-1
0
59
void insertAVL(AVLtree &p, elem
x, int &h)
{
if (p==NULL)
{
p = new node;
p->data = x;
p->left = p->right = NULL;
p->bal=0;
h=1;
//true
}
else
}
if (x<p->data)
{
insertAVL(p->left, x, h);
if (h) Xem lại cân bằng trái tại p;
}
else
if (x>p->data)
{
insertAVL(p->right, x, h);
if (h) Xem lại cân bằng
phải tại p;
}
else
h=0;
//false
60
Xem lại thế cân bằng trái
switch (p->bal)
{
case 1: p->bal = 0; h = 0; break;
case 0: p->bal = -1; break;
case -1:
{
AVLtree p1 = p->left;
if (p1->bal ==-1)
{
p->left = p1->right;
p1->right = p;
p->bal = 0;
p = p1; //p->bal =0; h=0
}
else
{
AVLtree p2 = p1->right;
p->left = p2->right;
p2->right = p;
p1->right = p2->left;
p2->left = p1;
p->bal = (p2->bal==-1?1:0);
p1->bal = (p2->bal==1?-1:0);
p = p2; //p->bal =0; h=0
}
p->bal = 0;
h = 0;
}
}
61
Xem lại thế cân bằng phải
• Trường hợp này đối xứng với trường hợp
xem lại thế cân bằng trái, chính vì vậy ta
chỉ cần thay
– left thành right
và ngược lại
– 1 thành -1
và ngược lại
• Tham khảo chương trình nguồn
62
Xóa phần tử
20
Xóa 35
10
5
0
3
0
0
6
30
12 -1
1
0
-1
7
11
9
0
25
23
0
35 0
0
27
-1
0
0
63
20
10
5
3
0
0
6
25
12 -1
1
0
7
11
9
-1
0
23
1
30 -1
0
27
0
0
64
Xóa phần tử
20
Xóa 35
10
5
0
3
0
-1
6
30
12 -1
1
0
-1
7
11
9
0
25
23
-1
-1
35 0
0
0
65
• Mất cân bằng tại 20
20
10
5
3
0
-1
6
25
12 -1
1
0
7
11
9
-1
23
0
1
30 0
0
0
66
10
5
3
20 0
1
0
0
0
6
7
25
-1 12
9
0 11
0
23
0
0
30
67
Xóa phần tử trên AVL
void deleteAVL(AVLtree &p, elem x, int &h)
{
if (p==NULL)
h = 0;
else
if (x<p->data)
{
deleteAVL(p->left, x, h);
else
{
AVLtree q = p;
if (p->right==NULL)
{
p = p->left;
h = 1;
}
else
if (p->left==NULL)
{
p = p->right;
h = 1;
}
else
{
del(p->left, q, h);
if (h)
Xem lại thế cân bằng trái tại p
}
delete q;
}
if (h) Xem lại thế cân bằng trái tại p
}
else
if (x>p->data)
{
deleteAVL(p->right, x, h);
if (h)
Xem lại thế cân bằng phải tại p
}
}
68
Tìm phần tử thay thế
void del(AVLtree &r, AVLtree &q, int &h)
{
if (r->right!=NULL)
{
del(r->right, q, h);
if (h) Xem lại thế cân bằng phải tại r
}
else
{
q->data = r->data;
10
q = r;
r = r->left;
h = 1;
5
}
}
20
30
15
12
40
25
35
69
Kiểm tra 10%
(đến 11g30)
• Minh họa quá trình hình thành cây AVL khi thêm
lần lượt các giá trị sau vào cây (chỉ rõ phép
quay- nếu có):
2, 10, 3, 7, 5, 16, 12, 18, 14, 9,
4, 15.
70