chúng như sau:
Nếu βj > m(𝑣∗,𝑣) thì đặt β = m(𝑣∗,𝑣) và nhãn của𝑣 là [𝑣∗, β ]. Ngược lại, ta giữ nguyên nhãn của𝑣. Sau đó quay lại Bước 3.
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 188 Thuật toán PRIM tìm CKNN của đồ thị cho bởi trọng số
• Trong phần này, giới thiệu về giải thuật tìm đường đi ngắn nhất giữa 2 đỉnh trên đồ thị có trọng số. • Nội dung gồm:
– Giới thiệu về bài toán – Thuật toán gán nhãn – Thuật toán Dijkstra
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 189 Tìm đường đi ngắn nhất
• Xét đồ thị G = (V, E) với |V| = n, |E| = m
• Với mỗi cạnh u, v∈E, có một giá trị trọng số A(u,v)
• Đặt A(u,v) = ∞ nếu u, v∉E
• Nếu dãy𝑣,𝑣,...,𝑣 là một đường đi trên G thì
–∑ 𝐴( 𝑣 ,𝑣) được gọi là độ dài của đường đi
• Bài toán: Tìm đường đi ngắn nhất từ đỉnh s đến đỉnh t của đồ thị G
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 190 Bài toán tìm đường đi ngắn nhất
• Thuật toán được mô tả như sau:
– Từ ma trận trọng số A(u,v), u,v∈V, tìm cận trên d[v] của khoảng cách từ s đến tất cả các đỉnh v∈V.
– Nếu thấy d[u] + A(u,v) < d[v] thì d[v] = d[u] + A(u,v) (làm tốt lên giá trị của d[v])
– Quá trình sẽ kết thúc khi không thể làm “tốt lên” được nữa – Khi đó d[v] sẽ cho ta giá trị ngắn nhất từ đỉnh s đến đỉnh v. – Giá trị d[v] được gọi là nhãn của đỉnh v
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 191 Thuật toán gán nhãn
• Tìm đường đi ngắn nhất từ A đến Z trong đồ thị G sau
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 192 Thuật toán gán nhãn – Ví dụ A B Z F C H 7 4 3 6 5 E D I
• Các bước thực hiện của giải thuật: – Bước 1: Gán cho nhãn đỉnh A là 0, d[A] = 0;
– Bước 2: Chọn cạnh có độ dài nhỏ nhất xuất phát từ A (cạnh AC), gán nhãn của đỉnh kề C với:
– d[C] = d[A] + A(A,C) = 0 + 5 = 5
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 193 Thuật toán gán nhãn – Ví dụ A B Z F C H 7 4 3 6 5 E D I
• Thuật toán này do E.Dijkstra, nhà toán học người Hà Lan, đề xuất năm 1959.
• Thuật toán tìm đường đi ngắn nhất từ đỉnh s đến các đỉnh còn lại được Dijkstra đề nghị áp dụng cho trường hợp đồ thị có hướng với trọng số không âm.
• Thuật toán được thực hiện trên cơ sở gán tạm thời cho các đỉnh. • Nhãn của mỗi đỉnh cho biết cận trên của độ dài đường đi ngắn
nhất tới đỉnh đó.
• Các nhãn này sẽ được biến đổi (tính lại) nhờ một thủ tục lặp, mà ở mỗi bước lặp một số đỉnh sẽ có nhãn không thay đổi, nhãn đó chính là độ dài đường đi ngắn nhất từ s đến đỉnh đó
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 194 Thuật toán Dijkstra
voidDijkstra(void)
//Đầu vào G =(V, E) với n đỉnh có ma trận trọng số A(u,v)>=0; s∈V //Đầu ra là khoảng cách nhỏ nhất từ s đến các đỉnh còn lại d[v]:v∈V //Truoc[v]: ghi lại đỉnh trước v trong đường đi ngắn nhất từ s đến v
{
//Bước 1: Khởi tạo nhãn tạm thời cho các đỉnh for( v∈V ){ d[v] = A(s,v); truoc[v]=s; } d[s]=0; T = V\{s}; //T là tập đỉnh có nhãn tạm thời //Bước lặp while(T !=Ø){ Tìm đỉnh u∈T sao cho d[u] = min { d[z] | z∈T}; T= T\{u}; //cố định nhãn đỉnh u*/ for( v∈T ){ //Gán lại nhãn cho các đỉnh trong T if(d[v] > d[u] + A(u,v)){ d[v] = d[u] + A(u,v); truoc[v] =u; } } } }
Giả mã của giải thuật Dijkstra
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 195
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 196
Ví dụ về giải thuật Dijkstra
V1 V2 V2 V6 V4 V3 V5 5 8 2 10 1
– Cho đồ thị G như hình vẽ, tìm đường từ V1->V6
–O(mlogn) Borůvka,Prim, Dijkstra, Kruskal,… –O(mlog logn) Yao (1975), Cheriton-Tarjan (1976) –O(m(m,n)) Fredman-Tarjan (1987)
–O(mlog(m,n))Gabow-Galil-Spencer-Tarjan (1986) –O(m(m,n)) Chazelle (JACM2000)
– Optimal Pettie-Ramachandran (JACM2002)
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 197 Độ phức tạp thuật toán
• Cây nhị phân • Cây đỏ đen
• B-Tree và cây nhiều nhánh
21/07/20 Bộ môn CNTT - Bài giảng Cơ sở toán học cho tin học 198 5.3. Một số loại cây đặc biệt và ứng dụng
• Một cây có gốc T được gọi là cây nhị-phân nếu mỗi đỉnh của T có nhiều nhất là 2 con. Với m=2, ta có một cây nhị phân.
• Trong một cây nhị phân, mỗi con được chỉ rõ là con bên trái hay con bên phải; con bên trái (t.ư. phải) được vẽ phía dưới và bên trái (t.ư. phải) của cha.
• Cây nhị phân có nhiều ứng dụng trong thực tế
• Tính GT của Biểu thức(a*b+c)*(d/e-f)
Cây nhị phân
199
Cài đặt Cây nhị phân
• Khai báo cấu trúc dữ liệu của cây nhị phân lưu biểu thức số học
Cấu trúc một nút trong cây:
typedef struct tagNode{ char *x;
struct tagNode *pLeft, *pRight; } Node;
Cấu trúc cây nhị phân:
typedef Node *TREE;
200
Hàm tính giá trị của biểu thức từ cây nhị phân double Value(TREE Root){
if (Root!=NULL)
if (Root->x[0]=='+' || Root->x[0]=='-' || Root->x[0]=='*' || Root->x[0]=='/') switch (Root->x[0]){
case '+': return Value(Root->pLeft)+Value(Root->pRight);break; case '-' : return Value(Root->pLeft)-Value(Root->pRight);break; case '*': return Value(Root->pLeft)*Value(Root->pRight);break; case '/' : return Value(Root->pLeft)/Value(Root->pRight);break; }
else return atof(Root->x); //atof là hàm đổi 1 xâu gồm các kí tự số sang số thực
}
201
DUYỆT CÂY NHỊ PHÂN
• Trong nhiều trường hợp, ta cần phải “điểm danh” hay “thăm” một cách có hệ thống mọi đỉnh của một cây nhị phân, mỗi đỉnh chỉ một lần. Ta gọi đó là việc duyệt cây nhị phân hay đọc cây nhị phân.
• Có nhiều thuật toán duyệt cây nhị phân, các thuật toán đó khác nhau chủ yếu ở thứ tự thăm các đỉnh.
• Cây nhị phân T có gốc r được ký hiệu là T(r). Giả sử r có con bên trái là u, con bên phải là v. Cây có gốc u và các đỉnh khác là mọi dòng dõi của u trong T gọi là cây con bên trái của T, ký hiệu T(u). Tương tự, ta có cây con bên phải T(v) của T. Một cây T(r) có thể không có cây con bên trái hay bên phải.
202
DUYỆT CÂY NHỊ PHÂN
203
Các thuật toán duyệt cây nhị phân
• 1) Thuật toán tiền thứ tự: Node-Left-Right
• 2) Thuật toán trung thứ tự: Left-Node-
Right
• 3) Thuật toán hậu thứ tự: Left-Right-Node
Thuật toán tiền thứ tự: Node-Left-Right • 1.Thăm nút gốc.
• 2.Duyệt cây con bên trái của T(r) theo tiền thứ tự. • 3.Duyệt cây con bên phải của T(r) theo tiền thứ
tự.
205
Node-Left-Right
void NLR(Node Root) { if (Root != NULL) { //Thăm nút gốc NLR(Root->Left); NLR(Root->Right); } } 206
Thuật toán trung thứ tự: Left-Node-Right
• 1.Duyệt cây con bên trái của T(r) theo trung thứ tự.
• 2.Thăm nút gốc
• 3.Duyệt cây con bên phải của T(r) theo trung thứ tự.
207
Left-Node-Right
void LNR(Node Root) { if (Root != NULL) { LNR(Root->Left); //Thăm nút gốc LNR(Root->Right); } } 208
Thuật toán hậu thứ tự: Left-Right-Node
• 1.Duyệt cây con bên trái của T(r) theo hậu thứ tự. • 2.Duyệt cây con bên phải của T(r) theo hậu thứ
tự.
• 3.Thăm nút gốc.
209
Left-Right-Node
void LRN(Node Root) { if (Root != NULL) { LRN(Root->Left); LRN(Root->Right); //Thăm nút gốc } } 210
• Cây nhị phân
• Bảo đảm nguyên tắc bố trí khoá tại mỗi nút:
– Các nút trong cây trái nhỏ hơn nút hiện hành – Các nút trong cây phải lớn hơn nút hiện hành
18
13 37
15 23 40
Ví dụ:
Khái niệm CNP TK
Ưu điểm của cây nhị phân tìm kiếm
• Nhờ trật tự bố trí khóa trên cây : – Định hướng được khi tìm kiếm • Cây gồm N phần tử :
– Trường hợp tốt nhất h = log2N – Trường hợp xấu nhất h = N
– Tình huống xảy ra trường hợp xấu nhất?
Cài đặt cây nhị phân tìm kiếm • Cấu trúc dữ liệu của 1 nút
typedefstruct tagTNode {
intKey; //trường dữ liệu là 1 số nguyên
structtagTNode *pLeft;
structtagTNode *pRight; }TNode;
• Cấu trúc dữ liệu của cây typedefTNode *TREE;
Các thao tác trên cây nhị phân tìm kiếm • Tạo 1 cây rỗng
• Tạo 1 nút có trường Key bằng x • Thêm 1 nút vào cây nhị phân tìm kiếm • Duyệt cây nhị phân tìm kiếm • Xoá 1 nút có Key bằng x trên cây • Tìm 1 nút có khoá bằng x trên cây
Tạo cây rỗng
• Cây rỗng -> địa chỉ nút gốc bằng NULL
voidCreateTree(TREE&T) {
T=NULL; }
Tạo 1 nút có Key bằng x
TNode*CreateTNode(intx) { TNode*p; p =newTNode; //cấp phát vùng nhớ động if(p==NULL) exit(1); // thoát else {
p->key = x; //gán trường dữ liệu của nút = x p->pLeft = NULL;
p->pRight = NULL; }
returnp; }
• Rằng buộc: Sau khi thêm cây đảm bảo là cây nhị phân tìm kiếm.
intinsertNode(TREE &T, Data X)
{ if(T)
{if(T->Key == X) return0;
if(T->Key > X)returninsertNode(T->pLeft, X);
else returninsertNode(T->pRight, X); }
T= new TNode;
if(T == NULL) return -1; T->Key = X;
T->pLeft =T->pRight = NULL;
return1; } Thêm một nút x 44 18 88 13 37 59 108 15 23 40 55 71 Thêm X=50 44 < X 88 > X 59 > X 50 55 > X
Minh họa thêm 1 phần tử vào cây
95 14 5 14 8 4 6 3 12 13 Minh hoạ thành lập 1 cây từ dãy số 9, 5, 4, 8, 6, 3, 14,12,13
Tìm nút có khoá bằng x (không dùng đệ quy)
TNode *searchNode(TREE Root, int x) { TNode *p = Root; while(p != NULL) { if(x == p->Key)returnp; else if(x < p->Key) p = p->pLeft; else p = p->pRight; } returnp; } Tìm nút có khoá bằng x (dùng đệ quy) TNode*SearchTNode(TREE T, int x)
{ if(T!=NULL) if(T!=NULL) { if(T->key==x) returnT; else if(x>T->key) returnSearchTNode(T->pRight,x); else returnSearchTNode(T->pLeft,x); } returnNULL; } 44 18 88 13 37 59 108 15 23 40 55 71 Tìm X=55 Tìm thấy X=55 55 55 Minh hoạ tìm một nút
Duyệt cây nhị phân tìm kiếm
• Có 6 phép duyệt cây như sau
– NLR (gốc, trái, phải) – NRL (gốc, phải, trái) – LNR (trái,gốc, phải) – RNL (Phải,gốc,trái) – LRN (trái, phải,gốc) – RLN (phải, trái,gốc)
Thuật toán duyệt cây nhị phân tìm kiếm
voidNLR(TREEt) { if(t!=NULL) { printf(“%d”,t->key);//Thăm nút gốc NLR(t->pLeft); NLR(t->pRight); } }
Bài tập cho phần duyệt cây
Viết chương trình thực hiện các yêu cầu sau: 1. Đếm số nút trên cây
2. Tình tổng tất cả các khóa trên cây 3. Đếm số nút lá trên cây
4. Đếm số nút có duy nhất 1 cây con 5. Đếm số nút có 2 cây con
6. Cho biết khoá lớn nhất trong cây là bao nhiêu 7. Cho biết khóa nhỏ nhất trong cây là bao nhiêu
Viết hàm đếm số nút trong cây voidDem(TREEt,int&s)
{ if(t!=NULL) if(t!=NULL) { if (t->pLeft==NULL&&t->pRight==NULL) //Đếm lá if (t->pLeft==NULL||t->pRight==NULL) //Đếm nút có 1 con if (t->pLeft!=NULL&&t->pRight!=NULL) //Đếm nút có 2 con s++; Dem(t->pLeft,s); Dem(t->pRight,s); } } Lời gọi hàm voidmain() { TREEt; ints=0; Dem(t,s);
printf(“so nut trong cay :=%d”,s); } Duyệt cây • NLR: 9, 2, 6, 1, 10, 8, 5, 3, 7, 12, 4. • LNR: 6, 2, 10, 1, 9, 3, 5, 8, 12, 7, 4. 9 8 2 1 6 10 5 3 7 4 12
Hủy 1 nút có khoá bằng X trên cây
• Hủy 1 phần tử trên cây phải đảm bảo điều kiện ràng buộc của Cây nhị phân tìm kiếm
• Có 3 trường hợp khi hủy 1 nút trên cây TH1: X là nút lá
TH2: X chỉ có 1 cây con (cây con trái hoặc cây con phải) TH3: X có đầy đủ 2 cây con
• TH1: Ta xoá nút lá mà không ành hưởng đến các nút khác trên cây • TH2: Trước khi xoá x ta móc nối cha của X với con duy nhất của X. • TH3: Ta dùng cách xoá gián tiếp
44
18 88
13 37 59 108
15 23 55 71
Hủy X=37
Minh hoạ hủy phần tử x có 1 cây con
Hủy 1 nút có 2 cây con
• Ta dùng cách hủy gián tiếp, do X có 2 cây con
• Thay vì hủy X ta tìm phần tử thế mạng Y. Nút Y có tối đa 1 cây con. • Thông tin lưu tại nút Y sẽ được chuyển lên lưu tại X.
• Ta tiến hành xoá hủy nút Y (xoá Y giống 2 trường hợp đầu) • Cách tìm nút thế mạng Y cho X: Có 2 cách
C1: Nút Y là nút có khoá nhỏ nhất (trái nhất) bên cây con phải X
C2: Nút Y là nút có khoá lớn nhất (phải nhất) bên cây con trái của X
4418 88 18 88 13 37 59 108 15 23 40 55 71 30 23 Xoá nút có trường Key = 18, lúc đó nút có khoá 23 là nút thế mạng
Minh họa hủy phần tử X có 2 cây con
Cài đặt thao tác xoá nút có trường Key = x voidDeleteNodeX1(TREE &T,int x)
{
if(T!=NULL)
{ if(T->Key<x)DeleteNodeX1(T->Right,x); else {
if(T->Key>x)DeleteNodeX1(T->Left,x); else //timthấy Node có trường dữ liệu= x { TNode *p;
p=T;
if (T->Left==NULL) T = T->Right; else { if(T->Right==NULL) T=T->Left;
else ThayThe1(p, T->Right);//tìm bên cây con phải }
delete p; } } }
else printf("Khong tim thay phan can xoa tu");}
Hàm tìm phần tử thế mạng void ThayThe1(TREE &p, TREE &T) { if(T->Left!=NULL) ThayThe1(p,T->Left); else { p->Key = T->Key; p=T; T=T->Right; }
Ví dụ
• Cho dãy số nguyên: 30, 15, 20, 18, 10, 12, 11, 40, 35, 33, 37, 36, 34
• Hãy vẽ hình dạng cây nhị phân tìm kiếm ( cây sau cùng) khi thêm lần lượt từng giá trị trên vào cây. • Hãy cho biết kết quả của phép duyệt cây theo thứ
tự LNR( trái, gốc, phải), LRN( trái, phải, gốc). • Hãy vẽ hình dạng của cây nhị phân khi tiến hành
xóa liên tiếp nút mang giá trị 12 và 30
235
Bài tập về Cây nhị phân tìm kiếm