Các phép toán cơ bản trên cây tìm kiếm nhị phân

Một phần của tài liệu Giáo Trình Cấu Trúc Dữ Liệu Và Thuật Toán (Trang 55)

1- Khởi tạo cây TKNP rỗng

Ta cho con trỏ quản lý nút gốc (Root) của cây bằng NIL.

2 - Tìm kiếm một nút có khóa cho trước trên cây TKNP

Để tìm kiếm 1 nút có khoá x trên cây TKNP, ta bắt đầu từ nút gốc bằng cách so sánh khoá của nút gốc với khoá x.

- Nếu nút gốc bằng null thì không có khoá x trên cây.

- Ngược lại, Nếu x bằng khoá của nút gốc thì giải thuật dừng và ta đã tìm được nút chứa khoá x.

- Nếu x lớn hơn khoá của nút gốc thì ta tiến hành việc tìm khoá x trên cây con bên phải.

- Nếu x nhỏ hơn khoá của nút gốc thì ta tiến hành việc tìm khoá x trên cây con bên trái.

Ví dụ: tìm nút có khoá 30 trong cây ở trong Hình 3.9 như sau:

20

10 35

- So sánh 30 với khoá nút gốc là 20, vì 30 > 20 vậy ta tìm tiếp trên cây con bên phải, tức là cây có nút gốc có khoá là 35.

- So sánh 30 với khoá của nút gốc là 35, vì 30 < 35 vậy ta tìm tiếp trên cây con bên trái, tức là cây có nút gốc có khoá là 22.

- So sánh 30 với khoá của nút gốc là 22, vì 30 > 22 vậy ta tìm tiếp trên cây con bên phải, tức là cây có nút gốc có khoá là 30.

- So sánh 30 với khoá nút gốc là 30, 30 = 30 vậy đến đây giải thuật dừng và ta tìm được nút chứa khoá cần tìm.

Giải thuật dưới đây trả về kết quả là con trỏ p trỏ tới nút chứa khoá x hoặc null nếu không tìm thấy.

// Viet phep toan tim kiem 1 khoa x tren cay tim kiem T //input:T searchtree; x:int;

//output: Dia chi cuar nut cos khoa la x Node; Node * Search(SearchTree T, int x)

{

Node* V=T; int found=0;

while ((V!=NULL)&& (found==0)) if (x<V->key) V->left ;

else if (x>V->key) V=V->right; else { found=1; return V; } }

Câu hỏi thảo luận:

Cây tìm kiếm nhị phân được tổ chức như thế nào để quá trình tìm kiếm được hiệu quả nhất nếu thông tin cần tìm kiếm không phải là khóa trên cây?

Nhận xét:

Giải thuật này sẽ rất hiệu quả về mặt thời gian nếu cây TKNP được tổ chức tốt, nghĩa là cây tương đối "cân bằng". Về cây cân bằng các bạn có thể tham khảo thêm trong các tài liệu tham khảo của môn học này.

3 - Thêm một nút có khóa x vào cây TKNP

Xét cây TKNP gốc T, hãy thêm vào T đỉnh có khóa là x nếu x chưa có trên cây. T sau khi thêm x vẫn thỏa mãn là cây TLNP

hai nút có cùng một khoá. Do đó nếu ta muốn thêm một nút có khoá x vào cây TKNP trước hết ta phải tìm kiếm để xác định có nút nào chứa khoá x chưa?

+ Nếu có thì giải thuật kết thúc (không làm gì cả!). + Ngược lại, sẽ thêm một nút mới chứa khoá x này.

Thêm khoá x vào cây TKNP đảm bảo cấu trúc cây TKNP không bị phá vỡ. Có nhiều cách để thêm, tuy nhiên để tránh phức tạp, người ta thường thực hiện thêm ở mức lá. Cách giải cụ thể như sau ta bắt đầu từ nút gốc bằng cách so sánh khóa cuả nút gốc với khoá x.

- Nếu nút gốc bằng Nil thì khoá x chưa có trên cây, do đó ta thêm khoá x vào cây.

- Nếu x bằng khoá của nút gốc thì giải thuật dừng, trường hợp này ta không thêm.

- Nếu x lớn hơn khoá của nút gốc thì ta tiến hành (một cách đệ qui) giải thuật này trên cây con bên phải.

- Nếu x nhỏ hơn khoá của nút gốc thì ta tiến hành (một cách đệ qui) giải thuật này trên cây con bên trái.

Ví dụ: thêm khoá 19 vào cây ở trong Hình 3.9, ta làm như sau:

- So sánh 19 với khoá của nút gốc là 20, vì 19 < 20 vậy ta xét tiếp đến cây bên trái, tức là cây có nút gốc có khoá là 10.

- So sánh 19 với khoá của nút gốc là 10, vì 19 > 10 vậy ta xét tiếp đến cây bên phải, tức là cây có nút gốc có khoá là 17.

- So sánh 19 với khoá của nút gốc là 17, vì 19 > 17 vậy ta xét tiếp đến cây bên phải. Nút con bên phải bằng NULL, chứng tỏ rằng khoá 19 chưa có trên cây, ta thêm nút mới chứa khoá 19 và nút mới này là con bên phải của nút có khoá là 17, ta thu được cây như Hình 3.10

Giải thuật

void InsertTree(SearchTree &T, int x) {

Node *V,*P,*N;

int found=0;

// cap phat 1 nut

N=(Node*)calloc(1,sizeof(Node)); 20

10 35

5 17 22 42

15 30

Hình 3.10 – Thêm khóa 19 vào cây TKNP

58 if (N!=NULL) { N->left=NULL; N->right=NULL; N->key=x;

if (T==NULL) T=N;// neu cay rong nut moi them chinh la goc cua cay else { V=T; while ((V!=NULL)&&(!found)) { if (x<V->key ) { P=V; V=V->left ; } else if (x>V->key ) { P=V; V=V->right ; } else found=1; } if (found==0) {

if (x<P->key) P->left =N;//them N vao ben trai cuar P if (x>P->key) P->right=N;

} }

}

else printf("\n Ko cap phat dc bo nho"); }

4 - Xóa một nút có khóa cho trước ra khỏi cây TKNP

Xét cây TKNP gốc T, khoá x. Nếu đỉnh có khoá x có trên T thì loại bỏ đỉnh này sao cho T sau khi loại bỏ x vẫn là cây TKNP

Cách giải:

- Nếu không tìm thấy nút chứa khoá x thì giải thuật kết thúc.

- Nếu tìm gặp nút được trỏ bởi P có chứa khoá x, ta có ba trường hợp sau: T H 1 : Nếu p là lá: ta thay nó bởi Nil.

T H 2 : Nếu p có một trong 2 con là  (rỗng):

- Treo cây con khác  vào vị trí của p (như hình dưới) - Giải phóng vùng nhớ được trỏ bởi p

T       X M P T       M T1

TH3: Đỉnh loại bỏ được trỏ bởi P có 2 con đều khác rỗng. Thay nút được trỏ bởi p bởi nút lớn nhất trên cây con trái của nó (nút cực phải của cây con trái) hoặc là nút bé nhất trên cây con phải của nó (nút cực trái của cây con phải). Rồi xóa nút cực phải (hoặc nút cực trái ), việc xoá nút này sẽ rơi vào một trong 2 trường hợp ở trên (TH1 hoặc TH2) .Trong hình dưới đây, ta thay x bởi khoá của nút cực trái của cây con bên phải rồi ta xoá nút cực trái này.

5. Nhập dữ liệu cho cây NPTK

void taocay(SearchTree &T) {

int x; T=NULL;

printf("\n Nhap x="); scanf("%d",&x);

while (x!=0)

{

InsertTree(T,x);

printf("\n Nhap x="); scanf("%d",&x); }

}

6. In dữ liệu trên cây

//dung thuat toan duyet cay theo thu tu truoc void Incay(SearchTree T) { if (T!=NULL) { printf("%5d",T->key); Incay(T->left); // printf("%5d",T->key); Incay(T->right); } } T       X M T1 P T2 y T       y M T1 P T2 x

7. Duyệt cây theo thứ tự trước, giữa và thứ tự sau void inorder(SearchTree T) { if (T!=NULL) { inorder(T->left); printf("%5d",T->key); inorder(T->right); } }

CHƯƠNG 4 MÔ HÌNH DỮ LIỆU ĐỒ THỊ 4.1 Định nghĩa đồ thị và các khái niệm

4.1 1. Định nghĩa đồ thị

Đồ thị là một cấu trúc rời rạc, dùng để mô tả một tập hợp các đối tượng rời rạc có mối quan hệ n - m với nhau (n,m ≥ 0). ký hiệu đồ thị G là G=< V, E>, trong đó:

+ V là tập các đỉnh ( vertices)

+ E là tập các cạnh/cung ( Edges): Tập các cặp (u,v) mà u,v là hai đỉnh thuộc V

Dựa và đặc tính của tập E, ta có:

+ G là đơn đồ thị: Nếu giữa 2 đỉnh u,v bất kỳ có nhiều nhất 1 cạnh/cung + G là đa đồ thị: Nếu giữa 2 đỉnh u,v bất kỳ có thể có nhiều hơn 1 cạnh/cung

+ G là đồ thị vô hướng: Nếu E gồm các cặp (u,v)=(v,u), gọi là các cạnh

+ G là đồ thị có hướng: Nếu E là tập các cặp (u,v)<>(v,u), gọi là các cung. + G là đồ thị có trọng số: nếu các cạnh/cung của nó giá trị

4.1.2 Các khái niệm cơ bản trên đồ thị

Cạnh liên thuộc, đỉnh kề , bậc:

Nếu e = (u,v) là một cạnh thuộc E thì ta nói u,v kề nhau, và e là liên thuộc với u và v

 Nếu e =(u,v) là một cung thì ta nói u nối tới v và v nối từ u. Cung e đi ra khỏi đỉnh u (đầu ra) và đi vào đỉnh v (đầu vào). ta cũng nói u kề v

 Bậc của v (deg(v)) là số cạnh liên thuộc với v

b) Đường đi và chu trình

 Một đường đi P = (v1, v2, ...., vp) mà cạnh (vi-1, vi) thuộc E, với i = 2...p;  P là đường đi đơn, nếu tất cả các đỉnh trên đường đi không trùng nhau  Đường đi con của P là một đoạn liên tục dọc theo P

 P được gọi là chu trình nếu v1 = vp

 Một đồ thị được gọi là liên thông nếu với mọi cặp (u,v) đều có đường đi từ u đến v

4.2 Các phép toán cơ bản trên đồ thị

Các phép toán được cơ bản của đồ thị: - Đọc nhãn của đỉnh. - Đọc nhãn của cạnh. - Thêm một đỉnh vào đồ thị. - Thêm một cạnh vào đồ thị. - Xoá một đỉnh. - Xoá một cạnh.

- Lần theo (navigate) các cung trên đồ thị để đi từ đỉnh này sang đỉnh khác. Thông thường trong các giải thuật trên đồ thị, ta thường phải thực hiện một thao tác

nào đó với tất cả các đỉnh kề của một đỉnh, tức là một đoạn giải thuật có dạng sau:

For (mỗi đỉnh w kề với v)

{ thao tác nào đó trên w }

Để cài đặt các giải thuật như vậy ta cần bổ sung thêm khái niệm về chỉ số của các đỉnh kề với v. Hơn nữa ta cần định nghĩa thêm các phép toán sau đây:

- FIRST(v) trả về chỉ số của đỉnh đầu tiên kề với v. Nếu không có đỉnh nào

kề với v thì $ được trả về. Giá trị $ được chọn tuỳ theo cấu trúc dữ liệu cài đặt đồ thị.

- NEXT(v,i) trả về chỉ số của đỉnh nằm sau đỉnh có chỉ số i và kề với v. Nếu

không có đỉnh nào kề với v theo sau đỉnh có chỉ số i thì $ được trả về. - VERTEX(i) trả về đỉnh có chỉ số i.

4.3 Biểu diễn đồ thị

Một số cấu trúc dữ liệu có thể dùng để biểu diễn đồ thị. Việc chọn cấu trúc dữ liệu nào là tuỳ thuộc vào các phép toán trên các cung và đỉnh của đồ thị. Hai cấu trúc thường gặp là biểu diễn đồ thị bằng ma trận kề (adjacency matrix) và biểu diễn đồ thị bằng danh sách các đỉnh kề (adjacency list).

4.3.1. Biểu diễn đồ thị bằng ma trận kề

Ta dùng một mảng hai chiều, chẳng hạn mảng A, kiểu boolean để biểu diễn các đỉnh kề. Nếu đồ thị có n đỉnh thì ta dùng mảng A có kích thước nxn. Giả sử các đỉnh được đánh số 1..n thì A[i,j] = true, nếu có đỉnh nối giữa đỉnh thứ i và đỉnh thứ j, ngược lại thì A[i,j] = false. Rõ ràng, nếu G là đồ thị vô hướng thì ma trận kề sẽ là ma trận đối xứng.

Chẳng hạn đồ thị trong Hình 4.2 có biểu diễn ma trận kề như sau: j i 1 2 3 4

1 true true true false

2 true true true true

3 true true true true

4 false true true true

Ta cũng có thể biểu diễn true là 1 còn false là 0. Với cách biểu diễn đồ thị bằng ma trận kề như trên chúng ta có thể định nghĩa chỉ số của đỉnh là số nguyên xác định duy nhất đỉnh đó.

 Dạng biểu diễn đồ thị trên máy tính bằng ma trận kề: Xem như bài tập dành cho bạn đọc

 Cài đặt các phép toán: xem như bài tập dành cho bạn đọc

Nhận xét:

- G là đồ thị vô hướng thì ma trận kề tương ứng biểu diễn G là đồ thị đối xứng nhau qua đường chéo chính (a[i,j] = a[j,i])

- Kích thước ma trân luôn là N2 dẫn đến lãng phí bộ nhớ, đặc biệt nếu G là ma trận thưa (số đỉnh lớn, số cạnh/cung ít)

- G là ma trận trọng số, thay giá trị 1 của ma trận kề bởi giá trị trọng số tương ứng => gọi là ma trận trọng số

4.3.2. Biểu diễn đồ thị bằng danh sách các đỉnh kề

Trong cách biểu diễn này, ta sẽ lưu trữ các đỉnh kề với một đỉnh i trong một danh sách liên kết theo một thứ tự nào đó. Như vậy ta cần một mảng một chiều G có n phần tử để biểu diễn cho đồ thị có n đỉnh. G[i] là con trỏ trỏ tới danh sách các đỉnh kề với đỉnh i.

a) Dạng cài đặt: Bạn đọc có thể xem các tài liệu tham khảo của giáo trình

b) Cài đặt các phép toán: Xem như bài tập dành cho bạn đọc 4.4 Các phép duyệt đồ thị (TRAVERSALS OF GRAPH)

Trong khi giải qu yết các vấn đề thực tế, nhiều bài toán được mô hình hoá bằng mô hình đồ thị, khi xâ y dựng các thao tác xử lý trên đồ thị ta cần đi qua các đỉnh và các cung của đồ thị một cách có hệ thống. Việc đi qua các đỉnh của đồ thị một cách có hệ thống như vậy gọi là duyệt đồ thị. Có hai phép duyệt đồ thị phổ biến đó là duyệt theo chiều sâu và duyệt theo chiều rộng.

4.4.1. Duyệt theo chiều sâu (depth-first search)

Giả sử xét đồ thị G=(V,E) với các đỉnh ban đầu được đánh dấu là chưa duyệt (unvisited). Từ một đỉnh v nào đó ta bắt đầu duyệt như sau: đánh dấu v đã duyệt, với mỗi đỉnh w chưa duyệt kề với v, ta thực hiện đệ qui quá trình trên cho w. Sở dĩ cách duyệt này có tên là duyệt theo chiều sâu vì nó sẽ duyệt theo một hướng nào đó sâu nhất có thể được.

4.4.2. Duyệt theo chiều rộng (breadth-first search)

Xét đồ thị G với các đỉnh ban đầu được đánh dấu là chưa duyệt (unvisited). Từ một đỉnh v nào đó ta bắt đầu duyệt như sau: đánh dấu v đã được duyệt, kế đến là duyệt tất cả các đỉnh kề với v. Khi ta duyệt một đỉnh v rồi đến đỉnh w thì các đỉnh kề của v được duyệt trước các đỉnh kề của w, vì vậy ta dùng một hàng để lưu trữ các nút theo thứ tự được duyệt để có thể duyệt các đỉnh kề với chúng. Ta cũng dùng mảng một chiều mark để đánh dấu một nút là đã duyệt hay chưa, tương tự như duyệt theo chiều sâu.

Cụ thể: Xét đồ thị G = (V, E) , u thuộc V là đỉnh xuất phát. Nguyên tắc duyệt (thăm) đồ thị G như sau:

Thăm u, rồi thăm đến các đỉnh kề u, rồi đến các đỉnh chưa được thăm kề với các đỉnh này và tiếp tục lặp lại với các đỉnh mới.

Phép duyệt kết thúc khi mọi đỉnh liên thông với u đều được thăm =) còn gọi là duyệt theo vết dầu loang

4.5Một số bài toán ứng dụng trên đồ thị: 4.5.1 Bài toán tìm cây khung với giá cực tiểu 4.5.1 Bài toán tìm cây khung với giá cực tiểu

Cho đồ thị G = (V, E) là đồ thị vô hướng, Đồ thị T = (V, F) trong đó F là tập con của E => T được gọi là cây khung của đồ thị G nếu T là đồ thị liên thông phi chu trình

Nếu G là đồ thị vô hướng có trọng số: Giá của cây khung là tổng trọng số của các cạnh trong nó

Nhận xét:

Cùng một đồ thị G ta có thể tìm được nhiều cây khung. Việc tìm cây khung có giá cực tiểu có nhiều ý nghĩa trong các bài toán ứng dụng thực tế.

Các thuật toán tìm cây khung với giá cực tiêu, ứng dụng của chúng (xem như bài tập danh cho bạn đọc)

G là đồ thị vô hướng liên thông có thể có nhiều cây khung. Nếu G là đồ thị trọng số

Tìm cây khung nhỏ nhất có ý nghĩa thực tiễn? Tìm như thế nào? Dành cho bạn đọc

4.5.2 Bài toán tìm đường đi ngắn nhất trên đồ thị

Cho một đồ thị G, Tìm đường đi ngắn nhất: - Từ một đỉnh đến một đỉnh

- Từ một đỉnh đến các đỉnh còn lại - Giữa mọi cặp đỉnh trên đồ thị

Một phần của tài liệu Giáo Trình Cấu Trúc Dữ Liệu Và Thuật Toán (Trang 55)

Tải bản đầy đủ (PDF)

(76 trang)