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

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 63)

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ị

Phần thuật toán tìm đường đi ngắn nhất như trên xem như bài tập dành cho bạn đọc

CÂU HỎI VÀ BÀI TẬP CHƯƠNG 4 Câu hỏi 1: Hãy trình bày về cấu trúc đồ thị

- Các CTDL biểu diễn đồ thị

- Nêu các hạn chế khi biểu diễn đồ thị bằng ma trận lân cận kề - Các phương pháp duyệt đồ thị: Theo chiều sâu, Theo chiều rộng

Câu hỏi 2 Cho đồ thị như hình vẽ:

Hãy: Mô tả và cài đặt CTDL biểu diễn đồ thị bằng ma trận lân cận (kề). Biểu diễn đồ thị danh sách lân cận (kề)

Câu hỏi 3: Cho đồ thị như hình vẽ:

B C E A D a) 1 2 3 4 5

Hãy:

Viết kết quả sau khi duyệt đồ thị trên bằng phương pháp duyệt theo chiều rộng, chiều sâu

Bài tập: Viết các giải thuật tương ứng thực hiện các công việc sau:

1. Nhập dữ liệu cho đồ thị

2. Tìm các đường đi giữa một cặp đỉnh nào đó 3. Kiểm tra tính liên thông của đồ thị

4. Tìm các thành phần liên thông của đồ thị 5. Tìm các cây khung của đồ thị

6. Tìm số đỉnh trên đồ thị V1 V2 V3 V5 V6 V7 V11 V8 V4 V10 V9

CHƯƠNG 5 MÔ HÌNH DỮ LIỆU TẬP HỢP 5.1. Khái niệm tập hợp

Tập hợp trong toán học là một cấu trúc rời rạc cơ bản để từ đó dựng lên các cấu trúc rời rạc khác như: Các tổ hợp – là những tập hợp không sắp thứ tự của các phần tử; đồ thi – là tập hợp các đỉnh và các cạnh nối các đỉnh đó, cây – là một tập hợp các đỉnh và các cạnh nối hai đỉnh có quan hệ phân cấp, ... Tập hợp được dùng để mô hình hoá hay biểu diễn một nhóm bất kỳ các đối tượng trong thế giới thực cho nên nó đóng vai trò rất quan trọng trong mô hình hoá cũng như trong thiết kế các giải thuật.

 Tập hợp là một cấu trúc rời rạc được nói là chứa các phần tử của nó.

 Tập hợp thường dùng để gom nhóm các phần tử có các tính chất chung lại với nhau, nhưng nó cũng có thể chứa các phần tử chẳng có mối quan hệ gì với nhau.

Ví dụ1 : A={a,2,Fred, Lan}.

Ví dụ 2: Tất cả các sinh viên vừa mới nhập trường lập nên một tập hợp, hoặc các sinh viên đang học môn CTDL& thuật toán lập nên một tập hợp, hoặc những sinh viên vừa mới nhập học và những sinh viên đang học môn CTDL & TT cũng lập nên một tập hợp

 Một tập hợp có thể là vô hạn hoặc hữu hạn

Để mô tả một tập hợp, trong toán học thường có các cách sau để xác định một tập hợp A

(1) Dùng biểu đồ Ven là một đường cong khép kín, các điểm trong đường cong đó chỉ các phần tử của tập hợp.

(2) Liệt kê tất cả các phần tử của tập hợp A. Ví dụ: A = {1, 2, 3}

(3) Nêu lên các đặc trưng cho biết chính xác một đối tượng bất kỳ có là một phần tử của tập A hay không. Ví dụ: A = {x | x là số nguyên chẵn}

5. 2 Mô hình dữ liệu tập hợp

Trong thiết kế thuật toán, có thể sử dụng tập hợp như một mô hình dữ liệu. Khi đó, ngoài các phép toán hợp, giao, hiệu, chúng ta phải cần đến nhiều các phép toán khác. Sau đây chúng ta sẽ đưa ra một số phép toán cơ bản quan trọng nhất, các phép toán này sẽ được mô tả bởi các thủ tục hoặc hàm:

1- Thủ tục UNION(A,B,C) nhận vào 3 tham số là A,B,C; Thực hiện phép toán lấy hợp của hai tập A và B và trả ra kết quả là tập hợp C = A B.

2 -Thủ tục INTERSECTION(A,B,C) nhận vào 3 tham số là A,B,C; Thực hiện phép toán lấy giao của hai tập A và B và trả ra kết quả là tập hợp C = A ∩ B. 3 - Thủ tục DIFFERENCE(A,B,C) nhận vào 3 tham số là A,B,C; Thực hiện phép

toán lấy hợp của hai tập A và B và trả ra kết quả là tập hợp C = A\B

5 - Hàm MEMBER(x,A) cho kết quả kiểu logic (đúng/sai) tùy theo x có thuộc A hay không. Nếu x  A thì hàm cho kết quả là đúng, ngược lại cho kết quả sai. 5 - Thủ tục MAKENULLSET(A) tạo tập hợp A tập rỗng

7 - Thủ tục DELETESET(x,A) xoá x khỏi tập hợp A 8 - Thủ tục ASSIGN(A,B) gán A cho B ( tức là B:=A ) 9 - Hàm MIN(A) cho phần tử bé nhất trong tập A

10 - Hàm EQUAL(A,B) cho kết quả TRUE nếu A=B ngược lại cho kết quả FALSE

=> Vấn đề tiếp theo đặt ra là: Ta cần biểu diễn tập hợp như thế nào trong máy tính để các phép toán được thực hiện với hiệu quả cao

5.3 Biểu diễn tập hợp trên máy tính và cài đặt các phép toán trên tập hợp

Các phần tử của tập hợp có thể là các đối tượng có kiểu dữ liệu cơ bản hoặc là các đối tương có kiểu dữ liệu phức tạp, các đối tượng này có thể được biểu diễn bởi bản ghi, các trường là các thuộc tính của đối tượng.

Có nhiều phương pháp để cài đặt mô hình dữ liệu tập hợp trong MT. Trong từng áp dụng, tuỳ thuộc vào các phép toán cần thực hiện, giá trị các phần tử trong tập hợp và kích cỡ (số các phần tử của tập hợp) mà ta lựa chọn cách cài đặt sao cho các phép toán thực hiện có hiệu quả nhất. Chẳng hạn nếu chúng ta thường xuyên sử dụng phép thêm vào và loại bỏ các phần tử trong tập hợp thì chúng ta sẽ tìm cách cài đặt hiệu quả cho các phép toán này. Còn nếu phép tìm kiếm một phần tử xảy ra thường xuyên thì ta có thể phải tìm cách cài đặt phù hợp để có hiệu quả tốt nhất. Ta xét các phương pháp cài đặt tập hợp sau:

5.3.1.Cài đặt tập hợp bởi vectơ bit

Giả sử, xét tập hợp A gồm các số nguyên thuộc phạm vi từ 1 đến n (hoặc được mã hóa thành các số nguyên thuộc phạm vi từ 1 -> n). Khi đó ta có thể dùng véc tơ bit (mảng boolean) để biểu diễn tập A : (A[1], A[2], ... ,A[n]), trong đó thành phần thứ i : A[i] = true nếu i  A, A[i] = false nếu iA.

Ví dụ: Giả sử các phần tử của tập hợp được lấy trong các số nguyên từ 1 đến 10, khi đó tập hợp được biểu diễn bởi một mảng một chiều có 10 phần tử với các giá trị phần tử thuộc kiểu logic. Chẳng hạn tập hợp A={1,3,5,8} được biểu diễn trong mảng có 10 phần tử như sau:

1 2 3 4 5 6 7 8 9 10

1 0 1 0 1 0 0 1 0 0

Nhận xét:

Với cách khai báo này các phép toán trên tập được thực hiện dễ dàng bằng cách sử dụng các phép toán logic trong ngôn ngữ lập trình. Ví dụ thêm x vào A chỉ việc cho A[x] = true , để xác định xem x có là phần tử của tập A không ta chỉ cần biết A[x] là

true hay false, ……. ;

Danh sách kế tiếp (mảng)(2) Cài đặt tập hợp bởi

danh sách

Cài đặt tập hợp bởi véc tơ bit (1)

Danh sách liên kết (3) Danh sách được sắp (5)

5.3.2 Cài đặt tập hợp bởi mảng

Giả sử số phần tử của tập hợp không vượt quá một hằng nào đó Maxsize, khi đó ta có thể cài đặt tập hợp bởi mảng. Do kích thước mảng bị hạn chế, việc thực hiện các phép tính hợp, chèn phần tử vào tập hợp có thể dẫn đến một tập hợp có số phần tử vượt quá cỡ của mảng. Do đó khi sử dụng cách cài đặt này ta phải đặt Maxsize cho phù hợp để tiết kiệm bộ nhớ và tránh bị tràn

5.3.3 Cài đặt bởi danh sách liên kết hoặc danh sách được sắp

Tập hợp cũng có thể cài đặt bằng danh sách liên kết, trong đó mỗi phần tử của danh sách là một thành viên của tập hợp. Không như biểu diễn bằng vectơ bít, sự biểu diễn này dùng kích thước bộ nhớ tỉ lệ với số phần tử của tập hợp chứ không phải là kích thước đủ lớn cho toàn thể các tập hợp đang xét. Hơn nữa, ta có thể biểu diễn một tập hợp bất kỳ. Mặc dù thứ tự của các phần tử trong tập hợp là không quan trọng nhưng nếu một danh sách liên kết có thứ tự nó có thể trợ giúp tốt cho các phép duyệt danh sách. Chẳng hạn nếu tập hợp A được biểu diễn bằng một danh sách có thứ tự tăng thì hàm MEMBER(x,A) có thể thực hiện việc so sánh x một cách tuần tự từ đầu danh sách cho đến khi gặp một phần tử y ≥ x chứ không cần so sánh với tất cả các phần tử trong tập hợp.

Một ví dụ khác, chẳng hạn ta muốn tìm giao của hai tập hợp A và B có n phần tử. Nếu A,B biểu diễn bằng các danh sách liên kết chưa có thứ tự thì để tìm giao của A và B ta phải tiến hành như sau: for (mỗi x thuộc A ) { Duyệt danh sách B xem x có thuộc B không. Nếu có thì x thuộc giao của hai tập hợp A và B; }. Rõ ràng quá trình này có thể phải cần đến n x m phép kiểm tra (với n,m là độ dài của A và B).

Nếu A, B được biểu diễn bằng danh sách có thứ tự tăng thì đối với một phần tử eA ta chỉ tìm kiếm trong B cho đến khi gặp phần tử x ≥ e. Quan trọng hơn nếu f đứng ngay sau e trong A thì để tìm kiếm f trong B ta chỉ cần tìm từ phần tử x trở đi chứ không phải từ đầu danh sách lưu trữ tập hợp B.

Việc cài đặt tập hợp bởi danh sách liên kết sẽ khắc phục hạn chế về không gian khi sử dụng mảng. Tuy nhiên trong cách cài đặt này, việc thực hiện các phép toán trên tập hợp sẽ phức tạp hơn

b) Các phép toán cơ bản trên tập hợp 1 - Thủ tục UNION

Giải sử xét phép tìm hợp của hai tập A và B, kết quả là tập C. Muốn tìm hợp của A và B, ta chép B vào C, sau đó duyệt A, với mỗi e của A mà e không thuộc C thì đưa nó vào C.

2 - Thủ tục INTERSECTION

Xét phép tìm giao của hai tập A và B, kết quả là tập C. Để tìm giao của A và B ta duyệt tập A, với mỗi phần tử e của A ta tìm nó trong B, nếu thấy thì đưa vào C, nếu không thấy thì duyệt phần tử tiếp theo trong A.

3- Thủ tục gán ASSIGN(A,B): Chép các các phần tử của tập A sang tập B,

Duyệt các lần lượt các phần tử trong A để chép sang B

Hàm trả ra phần tử đầu danh sách (Nếu danh sách được sắp tăng dần theo giá trị của các phần tử).

6- Toán tử DELETESET là hoàn toàn giống như DELETE_LIST.

7 - Phép INSERTSET(x,A) cũng tương tự INSERT_LIST tuy nhiên ta phải chú

ý rằng khi xen x vào A phải đảm bảo thứ tự của danh sách được sắp.

8- Thủ tục DIFFERENCE(A,B,C) nhận vào 3 tham số là A,B,C; Thực hiện phép

toán lấy hợp của hai tập A và B và trả ra kết quả là tập hợp C = A\B

Cách làm:

Duyệt các phần tử trong A, với mỗi phần tử e  A, ta kiểm tra xem e có thuộc B không? Nếu không thuộc B, ta thêm e vào C, ngược lại ta duyệt phần tử e tiếp theo trong A

9- Hàm MEMBER(x,A) cho kết quả kiểu logic (đúng/sai) tùy theo x có thuộc

A hay không. Nếu x  A thì hàm cho kết quả là đúng, ngược lại cho kết quả sai.

Cách làm:

Duyệt danh tử đầu đến cuối tập A hoặc duyệt đến khi tìm thấy x thì dừng.

10 - Thủ tục MAKENULLSET(A) tạo tập hợp A tập rỗng: A:= nil

11 - Hàm EQUAL(A,B) cho kết quả TRUE nếu A=B ngược lại cho kết quả FALSE:

Kiểm tra các phần tử trong A có thuộc B không và ngược lại để kết luận A có bằng B không.

Cài đặt cụ thể các phép toán từ 2->10 xem như bài tập dành cho bạn đọc

5.4 Từ điển (Dictionary)

Từ điển là một kiểu dữ liệu trừu tượng, là một tập hợp đặc biệt, trong đó chúng ta chỉ quan tâm đến các phép toán InsertSet, DeleteSet, Member và MakeNullSet. Sở dĩ chúng ta nghiên cứu từ điển là do trong nhiều ứng dụng không sử dụng đến các phép toán hợp, giao, hiệu ...của hai tập hợp. Ngược lại ta cần một cấu trúc làm sao cho việc tìm kiếm, thêm và bớt phần tử có phần hiệu quả nhất gọi là từ điển. Chúng ta cũng chấp nhận MakeNullSet như là phép khởi tạo cấu trúc từ điể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 63)

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

(76 trang)