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