Trần Minh Tước, khóa luận tốt nghiệp “LẬP TRÌNH MÔ PHỎNG MỘT SỐ THUẬT TOÁN TRÊN ĐỒ THỊ” được hoàn thành, không trùng với bất cứ khóa luận nào khác.. Trần Minh Tước, chúng tôi xác định nh
Trang 1NGUYỄN THỊ CHIỀU
LẬP TRÌNH MÔ PHỎNG MỘT SỐ THUẬT TOÁN TRÊN ĐỒ THỊ
Chuyên ngành: Khoa học máy tính
Trang 2mình tới TS Trần Minh Tước đã tận tình giúp đỡ tôi trong quá trình hoàn
thành khóa luận này
Tôi xin chân thành cảm ơn Ban giám hiệu, phòng Sau đại học, trường Đại học Sư phạm Hà Nội 2 cũng như toàn thể các thầy cô giáo trong trường
đã tạo điều kiện thuận lợi trong quá trình tôi học tập và nghiên cứu
Trong quá trình thực hiện công tác nghiên cứu không tránh khỏi những hạn chế và thiếu sót, tôi xin chân thành cảm ơn những ý kiến đóng góp của các thầy giáo, cô giáo và các bạn học viên
Hà Nội, tháng 12 năm 2014
Tác giả
Nguyễn Thị Chiều
Trang 3LỜI CAM ĐOAN
Tôi xin cam đoan, kết quả luận văn hoàn toàn là kết quả của tự bản thân tôi tìm hiểu, nghiên cứu dưới sự hướng dẫn của TS Trần Minh Tước,
khóa luận tốt nghiệp “LẬP TRÌNH MÔ PHỎNG MỘT SỐ THUẬT TOÁN TRÊN ĐỒ THỊ” được hoàn thành, không trùng với bất cứ khóa luận nào khác
Trong quá trình làm khóa luận, tôi đã kế thừa những thành tựu của các nhà khoa học với sự trân trọng và biết ơn
Hà Nội, tháng 12 năm 2014
Tác giả
Nguyễn Thị Chiều
Trang 4MỤC LỤC
Trang
1.1 Tổng quan về thuật toán và mô phỏng thuật toán 10
1.3.2 Bài toán tìm luồng lớn nhất trong mạng vận tải 25
2.2.4 Thiết kế mô phỏng một số thuật toán trên đồ thị 38
Trang 53.1 Hệ thống phân cấp mô-đun 44
3.2.1 Mô phỏng thuật toán tìm kiếm DFS và BFS 46 3.2.2 Thuật toán tìm luồng cực đại Ford–Fulkerson 48
Trang 6DANH MỤC CÁC HÌNH
1.2 Biểu diễn đồ thị vô hướng G1 bằng ma trận vuông 16
1.3 Biểu diễn đồ thị có hướng G2 bằng ma trận vuông 16
1.7 Danh sách kề của mảng Adj gồm 12 phần tử 19
1.9 Đồ thị vô hướng 6 đỉnh, đỉnh xuất phát S=1 22 1.10 Minh họa ví dụ về thuật toán DFS dạng bảng 23 1.11 Minh họa ví dụ về thuật toán BFS dạng bảng 25
1.12 Tìm luồng cực đại trong mạng đỉnh phát S và đỉnh thu t 28
1.20 Đồ thị liên thông 6 đỉnh sau khi đã được tô màu 36 2.1 Quy trình phân tích và thiết kế các nhiệm vụ trước khi mô phỏng 39 2.2 Mô-đun mô phỏng một số thuật toán trên đồ thị 39
Trang 72.3 Mô hình bài toán mô phỏng thuật toán 41
3.5 Mô phỏng thuật toán BFS ở đồ thị có hướng liên thông 46 3.6 Mô phỏng thuật toán BFS ở đồ thị có hướng không liên thông 47 3.7 Mô phỏng thuật toán DFS ở đồ thị vô hướng liên thông 47 3.8 Mô phỏng thuật toán DFS ở đồ thị vô hướng không liên thông 47 3.9 Thuật toán Ford – Fulkerson với đỉnh nguồn 6, đỉnh đích 2 48 3.10 Thuật toán Welsh–Powell với đồ thị liên thông 49 3.11 Thuật toán Welsh–Powell với đồ thị không liên thông 49
Trang 8MỞ ĐẦU
1 Lý do chọn đề tài
Những năm gần đây, ở Việt Nam môn Tin học đã được đưa vào chương trình của học sinh trung học cơ sở và trung học phổ thông như là một môn học chính thức Đối với khối học sinh chuyên Tin học và học sinh giỏi Tin học các vấn đề về đồ thị cũng đã được đề cập Tuy nhiên, lý thuyết đồ thị
và các thuật toán trên đồ thị vẫn là một vấn đề rộng và phức tạp Việc hiểu và cài đặt tốt các thuật toán đó đòi hỏi thời gian và công sức rất lớn
Như một phần của quá trình học thuật toán, việc mô phỏng các thuật toán như một công cụ trợ giúp người học theo dõi chi tiết các bước hoạt động của thuật toán và các thay đổi của cấu trúc dữ liệu trong suốt quá trình thực thi Từ đó giúp người học tư duy thuật toán nhanh hơn và ngày càng yêu thích giải thuật
Với sự bùng nổ của công nghệ thông tin, thì việc mô phỏng thuật toán ngày càng trở nên hữu ích và trở thành giáo cụ trực quan trong hầu hết các lĩnh vực, nhất là trong môi trường giáo dục như hiện nay
Trong phạm vi của luận văn tốt nghiệp thạc sĩ chuyên ngành khoa học máy tính, từ sự đề xuất hướng nghiên cứu và trực tiếp hướng dẫn của TS Trần Minh Tước, chúng tôi xác định nhiệm vụ của đề tài là nghiên cứu về một
số thuật toán cơ bản trên đồ thị, sau đó sử dụng một ngôn ngữ lập trình để triển khai xây dựng chương trình nhằm mô phỏng hoạt động của một số thuật
toán trên đồ thị Từ đó chúng tôi đặt tên luận văn là: “Lập trình mô phỏng một số thuật toán trên đồ thị” Nội dung luận văn được chia thành 3 chương:
Chương 1 Kiến thức chuẩn bị
Ở chương này chúng tôi trình bày tổng quan về thuật toán và mô phỏng thuật toán, một số khái niệm cơ bản về đồ thị và giới thiệu một số bài toán trên đồ thị
Trang 9Chương 2 Biểu diễn dữ liệu và thiết kế hệ thống
Chương này, chúng tôi nói về cách biểu diễn dữ liệu của chương trình, quá trình phân tích, thiết kế và xây dựng hệ thống mô phỏng các thuật toán trên
Chương 3 Cài đặt chương trình
Trong chương này chúng tôi trình bày nhóm các thành phần chức năng theo từng cấp độ của chương trình, tạo giao diện và hướng dẫn sử dụng chương trình
Với đề tài này, chúng tôi hy vọng rằng các bài giảng về đồ thị sẽ trực quan, dễ hiểu hơn Kết quả của đề tài cũng là sự thể hiện quá trình tập dượt nghiên cứu của tôi
2 Mục đích nghiên cứu
Mục đích chính của đề tài là:
- Nghiên cứu về các thuật toán trên đồ thị
- Tìm hiểu về Phân tích và Thiết kế hệ thống, Kỹ thuật lập trình
- Sử dụng một ngôn ngữ lập trình để triển khai xây dựng chương trình
mô phỏng các thuật toán trên đồ thị
3 Nhiệm vụ nghiên cứu
Tìm hiểu các thuật toán trên đồ thị
Xây dựng chương trình mô phỏng các thuật toán trên đồ thị
4 Đối tượng và phạm vi nghiên cứu
4.1 Đối tượng nghiên cứu
Trang 105 Phương pháp nghiên cứu
- Nghiên cứu lí thuyết
- Triển khai cài đặt chương trình:
+ Thiết kế các thuật toán trên đồ thị Biểu diễn dữ liệu
+ Viết chương trình mô phỏng các thuật toán trên đồ thị
+ Chạy thử nghiệm và lưu trữ các kết quả đạt được, đánh giá lại kết quả
6 Kết quả đạt được
Xây dựng được một chương trình mô phỏng quá trình thực hiện của các thuật toán nói trên Góp phần giúp cho việc giảng dạy các thuật toán một cách
dễ hiểu và trực quan nhất
Trang 11Chương 1 KIẾN THỨC CHUẨN BỊ
1.1 Tổng quan về thuật toán và mô phỏng thuật toán
1.1.1 Tổng quan về thuật toán
Khái niệm thuật toán
Thuật toán là một khái niệm cơ bản của Toán học và Tin học Hiểu một cách đơn giản, thuật toán là một tập các hướng dẫn nhằm thực hiện một công việc nào đó Giải một bài toán Tin học là việc đi tìm một lời giải cụ thể, tường minh để đưa ra Output của bài toán dựa trên Input đã cho Việc chỉ ra một cách tìm Output của bài toán được gọi là một thuật toán
Trong thực tế có nhiều cách mô tả khái niệm về thuật toán, dưới đây
là cách phát biểu được chọn để đưa vào sách giáo khoa Tin học phổ thông:
“Thuật toán là một dãy hữu hạn các thao tác được sắp xếp theo một trình tự nhất định để sau khi thực hiện dãy các thao tác đó, từ input ta có output cần tìm”
Các đặc trưng của thuật toán
Dựa trên khái niệm về thuật toán và ví dụ ở trên ta thấy các thao tác trong thuật toán phải được mô tả đủ chi tiết để một đối tượng cứ tiến hành thực hiện theo đúng thứ tự các thao tác đó là có thể cho ra output dựa trên input tương ứng Một thuật toán phải đảm bảo được các tính chất sau:
Tính xác định: Sau khi thực hiện một thao tác thì hoặc là thuật toán
kết thúc hoặc là có đúng một thao tác xác định để thực hiện tiếp theo
Tính đúng đắn: Sau khi thực hiện thuật toán ta phải nhận được đúng
Output cần tìm
Tính dừng: Thuật toán phải kết thúc sau một số hữu hạn lần thực hiện
Tính tổng quát: Thuật toán là đúng đắn với mọi bộ dữ liệu đầu vào
của bài toán
Trang 12- Dễ cài đặt: thuật toán đó liệu có chuyển được thành chương trình bằng một ngôn ngữ lập trình nào đó hay không
Trước khi xây dựng thuật toán cho một bài toán nào đó, trước tiên phải xác định được Input và Output là gì, thử trên một số ví dụ cụ thể để định hướng cho việc xây dựng thuật toán
Nói về thuật toán người ta thường quan tâm đến tính hiệu quả của nó Tính hiệu quả này được đánh giá dựa trên các chi phí về thời gian và không gian cần thiết để thực hiện thuật toán Trong phạm vi luận văn chúng tôi chỉ quan tâm đến việc mô phỏng thuật toán mà không đi sâu xem xét về các tiêu chí nêu trên
1.1.2 Mô phỏng thuật toán
Mô phỏng thuật toán là quá trình mô tả cấu trúc dữ liệu, các thao tác của một chương trình bằng đồ họa Mô phỏng thuật toán được thiết kế nhằm giúp người dùng hiểu thuật toán, đánh giá thuật toán và gỡ lỗi khi cài đặt chương trình
Một chương trình lập trình trên máy tính sẽ chứa các cấu trúc dữ liệu của thuật toán mà nó sẽ thực hiện Trong khi thực hiện chương trình đó, giá trị thực của cấu trúc dữ liệu thay đổi dựa trên từng bước hoạt động của thuật toán Mô phỏng thuật toán sử dụng đồ họa để biểu diễn sự thay đổi trạng thái của cấu trúc dữ liệu cho từng bước hoạt động của nó một cách trực quan, khoa học Trong suốt quá trình biểu diễn, người dùng có thể quan sát việc
Trang 13thực thi thuật toán theo từng bước để có thể biết chi tiết về thuật toán cũng như hiểu một cách tường tận về thuật toán đó
Yêu cầu đối với mô phỏng thuật toán
Mô phỏng đúng theo thuật toán: Để mô phỏng một thuật toán nào
đó, các bước thực hiện chỉ dẫn trên đồ họa phải phản ánh đúng theo nội dung của thuật toán đã đưa ra để đảm bảo rằng người học học đúng thuật toán mình yêu cầu
Cho phép thực hiện theo từng bước: Thông thường, khi học một
thuật toán phải dùng đến chương trình mô phỏng để minh họa trong lúc học tập hoặc nghiên cứu thì thuật toán đó thường không phải là thuật toán đơn giản Vì vậy, việc để cho người dùng có thể hiểu được thuật toán thông qua chương trình mô phỏng thì chương trình đó phải hết sức “mềm dẻo”: ngoài việc cho phép người dùng đưa dữ liệu vào đúng theo chuẩn bị của họ thì nó còn có thể cho phép chạy thuật toán theo từng bước để họ có thể tiện theo dõi quá trình thay đổi dữ liệu cũng như kết quả của thuật toán sau mỗi bước thực hiện Ngoài ra, chương trình càng hiệu quả hơn nếu nó cho phép quan sát lại các bước đã thực hiện
Mô phỏng thuật toán phải có tính động: Vì công việc của mô phỏng là
mô tả sự thay đổi về cấu trúc dữ liệu sau mỗi bước thực hiện của thuật toán nên hình ảnh mô phỏng cấu trúc đó cũng phải thay đổi theo từng bước để người học nắm bắt được ý tưởng của thuật toán
Có thể thực thi với mọi bộ dữ liệu đầu vào: Thường các thuật toán để
dạy học cho học sinh giỏi và học sinh chuyên Tin đều là những thuật toán
“tốt” và có ứng dụng để giải quyết một lớp bài toán trong tin học Vì vậy, chương trình mô phỏng thuật toán cần đảm bảo chạy “tốt” đối với mọi bộ
dữ liệu đầu vào: trường hợp tốt, trường hợp xấu, trường hợp ngẫu nhiên…
Có sự phân cấp người học: Thông thường, mức độ tiếp thu trong
Trang 14một giờ học của học sinh không giống nhau, có những học sinh hiểu bài nhanh nhưng cũng có những học sinh nắm bắt bài chậm hơn Vì vậy, thuật toán mô phỏng cũng cần phải có những chức năng “mềm dẻo” với các đối tượng học
1.2 Một số khái niệm cơ bản về đồ thị
1.2.1 Đồ thị
Định nghĩa 1.1 Đồ thị là một cặp GV E, gồm hai tập hợp hữu hạn
V và E thoả mãn điều kiện E x y, | ,x y V x ; y
Phần tử của V được gọi là đỉnh, phần tử của E được gọi là cạnh của
đồ thị G
Trong định nghĩa này, mỗi phần tử của E là một tập hợp gồm hai phần
tử khác nhau thuộc V Như vậy, các đồ thị được xét ở đây là các đồ thị hữu hạn vô hướng, không có khuyên và không có cạnh bội
Người ta thường biểu diễn đồ thị trên mặt phẳng như sau: các vòng tròn nhỏ (rỗng hoặc đặc) biểu thị các đỉnh và nối hai đỉnh bằng một đường liên tục nếu hai đỉnh đó tạo thành một cạnh trong G
Hình 1.1 Biểu diễn đồ thị trên mặt phẳng
Ví dụ Ở hình 1.1, biểu diễn đồ thị của: GV E, với V a1 , ,a5,
Trang 15Định nghĩa 1.2 Đồ thị là một cặp GV E, với V là tập các đỉnh của
đồ thị, E là tập đỉnh các cặp đỉnh i j, mà các cặp này có thứ tự i j, khác trình j i, Cặp i j, gọi là cung của đồ thị có hướng
Ta nói GV E, là một đồ thị trên V ; tập đỉnh của G được ký hiệu là
V G , tập cạnh E G Để đơn giản, ta có thể viết “đỉnh vG” hay “cạnh
eG” chứ không nhất thiết phải viết “đỉnh v V G ” hay “cạnh eE G ”
Số đỉnh của đồ thị G được gọi là cấp (order) của G và được ký hiệu bằng G Số cạnh của nó được gọi là cỡ (size) của G và ký hiệu là G Như vậy G V còn G E Một đồ thị có cấp 0 hoặc 1 được gọi là tầm thường Hiển nhiên là nếu một đồ thị có cấp n thì cỡ m của nó thoả mãn
Hai đỉnh x y, của G được gọi là kề nhau hoặc là hàng xóm (của nhau)
nếu xy là một cạnh trong G Hai cạnh khác nhau e và f của G được gọi là
liền nhau nếu chúng có chung một đầu mút
Định nghĩa 1.3 Bậc của v , ký hiệu bởi deg v , là số cạnh liên thuộc với v , nghĩa là deg v E v Đỉnh bậc 0 được gọi là đỉnh cô lập
Bậc nhỏ nhất của G là số G min deg v |v V ; bậc lớn nhất của
G là số G max deg v |v V
Đối với đồ thị có hướng GV E, Xét một cung eE, nếu e u v,
thì ta nói u nối tới v và v nối từ u, cung e là đi ra khỏi đỉnh u và đi vào đỉnh
Trang 16v Đỉnh u khi đó được gọi là đỉnh đầu, đỉnh v được gọi là đỉnh cuối của cung e
Với mỗi đỉnh v trong đồ thị có hướng, ta định nghĩa: Bán bậc ra của v
ký hiệu deg v là số cung đi ra khỏi nó; bán bậc vào ký hiệu deg- (v) là số
cung đi vào đỉnh đó
Định nghĩa 1.4 Đồ thị không rỗng G được gọi là liên thông nếu hai đỉnh bất kỳ của nó luôn được nối với nhau bởi một đường trong G
Một đồ thị vô hướng gọi là liên thông nếu với mọi cặp đỉnh u v, ta
có u đến được v Một đồ thị có hướng gọi là liên thông mạnh
1.2.2 Biểu diễn đồ thị
Khi lập trình giải các bài toán được mô phỏng hoá bằng đồ thị, việc đầu tiên cần làm là tìm cấu trúc dữ liệu để biểu diễn đồ thị sao cho việc giải quyết bài toán được thuận tiện nhất
Có rất nhiều phương pháp biểu diễn đồ thị, sau đây là một số phương pháp phổ biến nhất
Trang 17Đối với đồ thị vô hướng G, thì mà trận kề tương ứng là ma trận đối
xứng a i j , a j i, , điều này không đúng với đồ thị có hướng
Nếu G là đồ thị vô hướng và A là ma trận kề tương ứng thì trên ma trận
A: Tổng các số trên hàng i = Tổng các số trên cột i = Bậc của đỉnh i deg i
Nếu G là đồ thị có hướng và A là ma trận kề tương ứng thì trên ma trận A: + Tổng các số trên hàng i = Bán bậc ra của đỉnh i deg i
+ Tổng các số trên cột i = Bán bậc vào của đỉnh i deg i
Trong trường hợp G là đơn đồ thị, ta có thể biểu diễn ma trận kề A
tương ứng là các phần tử logic a i j , TRUE nếu i j, E và
,
a i j FALSE nếu i j E
Trang 18Ưu điểm của ma trận kề
Đơn giản, trực quan, dễ cài đặt trên máy tính
Để kiểm tra xem hai đỉnh u v, của đồ thị có kề nhau hay không, ta chỉ việc kiểm tra bằng một phép so sánh: a u v , 0
Nhược điểm của ma trận kề
Bất kể số cạnh của đồ thị nhiều hay ít, ma trận kề luôn đòi hỏi 2
n ô nhớ để lưu các phần tử ma trận, điều đó gây lãng phí bộ nhớ dẫn tới việc không thể biểu diễn được đồ thị với số đỉnh lớn
Với một đỉnh u bất kỳ, nhiều khi ta phải xét tất cả các đỉnh v khác kề với nó, hoặc xét tất cả các cạnh liên thuộc với nó Trên ma trận kề việc đó
được thực hiện bằng cách xét tất cả các đỉnh v và kiểm tra điều kiện
, 0
a u v Như vậy, ngay cả khi đỉnh u là đỉnh cô lập (không kề với đỉnh
nào) hoặc đỉnh treo (chỉ kề với một đỉnh) ta cũng buộc phải xét tất cả các đỉnh và kiểm tra điều kiện trên dẫn tới lãng phí thời gian
b Danh sách cạnh
Trong trường hợp đồ thị có n đỉnh, m cạnh, ta có thể biểu diễn đồ thị
dưới dạng danh sách cạnh bằng cách liệt kê tất cả các cạnh của đồ thị trong một danh sách, mỗi phần tử của danh sách là một cặp u v, tương ứng với một cạnh của đồ thị (Trong trường hợp đồ thị có hướng thì mỗi cặp u v,
tương ứng với một cung, u là đỉnh đầu và v là đỉnh cuối của cung) Danh sách
được lưu trong bộ nhớ dưới dạng mảng hoặc danh sách móc nối Ví dụ đồ thị
ở hình 1.4 dưới đây:
Hình 1.4 Ví dụ đồ thị vô hướng 5 đỉnh
Trang 19Cài đặt trên mảng:
Hình 1.5 Đồ thị cài đặt trên mảng Cài đặt trên danh sách móc nối
Hình 1.6 Đồ thị cài đặt trên danh sách móc nối
Ưu điểm của danh sách cạnh
Trong trường hợp đồ thị thưa (số cạnh tương đối nhỏ: chẳng hạn
6
m n), cách biểu diễn bằng danh sách cạnh sẽ tiết kiệm được không gian
lưu trữ, bởi nó chỉ cần 2m ô nhớ để lưu danh sách cạnh
Trong một số trường hợp, ta phải xét tất cả các cạnh của đồ thị thì cài đặt trên danh sách cạnh làm cho việc duyệt các cạnh dễ dàng hơn
Nhược điểm của danh sách cạnh
Nhược điểm cơ bản của danh sách cạnh là khi ta cần duyệt tất cả các
đỉnh kề với đỉnh v nào đó của đồ thị, thì chẳng có cách nào khác là duyệt tất
cả các cạnh, lọc ra những cạnh có chứa đỉnh v và xét đỉnh còn lại Điều đó
khá tốn thời gian trong trường hợp đồ thị dày (nhiều cạnh)
Trang 20trong mảng lưu danh sách các đỉnh kề với đỉnh i: Với đồ thị Hình 1.4 danh sách các đỉnh kề với i sẽ là một mảng Adj gồm 12 phần tử:
Hình 1.7 Danh sách kề của mảng Adj gồm 12 phần tử
Để biết một đoạn nằm từ chỉ số nào đến chỉ số nào, ta có một mảng
Head lưu vị trí riêng Head i sẽ bằng chỉ số đứng liền trước đoạn thứ i Quy
ước Head n 1 bằng m Với đồ thị bên thì mảng Head 1 6 sẽ là
0,3,5,8,10,12
Các phần tử Adj Head i 1 Head i 1 sẽ chứa các đỉnh kề với
đỉnh i Lưu ý rằng với đồ thị có hướng gồm m cung thì cấu trúc này cần đủ chứa m phần tử, với đồ thị vô hướng m cạnh thì cấu trúc này cần phải đủ chứa 2m phần tử
Cách 2 Dùng các danh sách móc nối: Với mỗi đỉnh i của đồ thị, ta cho tương ứng với nó một danh sách móc nối các đỉnh kề với i, có nghĩa là tương ứng với một đỉnh i, ta phải lưu lại List i là chốt của một danh sách móc nối
Ví dụ Hình 1.4, các danh sách móc nối sẽ là:
Hình 1.8 Danh sách móc nối của đồ thị
Trang 21Ưu điểm của danh sách kề: Đối với danh sách kề, việc duyệt tất cả các
đỉnh kề với một đỉnh v cho trước là hết sức dễ dàng, cái tên “danh sách kề”
đã cho thấy rõ điều này Việc duyệt tất cả các cạnh cũng đơn giản vì một cạnh thực ra là nối một đỉnh với một đỉnh khác kề nó
Nhược điểm của danh sách kề: Danh sách kề yếu hơn ma trận kề ở
việc kiểm tra u v, có phải là cạnh hay không, bởi trong cách biểu diễn này
ta sẽ phải duyệt toàn bộ danh sách kề của u hay danh sách kề của v
Nhận xét
Đối với những thuật toán mà ta sẽ khảo sát, danh sách kề tốt hơn hẳn
so với hai phương pháp biểu diễn trước Chỉ có điều, trong trường hợp cụ thể
mà ma trận kề hay danh sách cạnh không thể hiện nhược điểm thì ta nên dùng ma trận kề hay danh sách cạnh bởi cài đặt danh sách kề có phần dài dòng hơn
Trên đây là nêu các cách biểu diễn đồ thị trong bộ nhớ máy tính, còn nhập dữ liệu cho đồ thị thì có nhiều cách khác nhau, dùng cách nào thì tuỳ ý Chẳng hạn nếu biểu diễn bằng ma trận kề mà cho nhập dữ liệu cả ma trận cấp
nxn (n là số đỉnh) thì khi nhập từ bàn phím sẽ mất rất nhiều thời gian, ta cho
nhập kiểu danh sách cạnh cho nhanh Chẳng hạn mảng A(nxn) là ma trận kề của một đồ thị vô hướng thì ta có thể khởi tạo ban đầu mảng A gồm toàn bộ
số 0, sau đó cho người sử dụng nhập các cạnh bằng cách nhập các cặp i j, ; chương trình sẽ tăng A i j , và A j i , lên 1 Việc nhập có thể cho kết thúc khi người sử dụng nhập giá trị i 0
Trong nhiều trường hợp đủ không gian lưu trữ, việc chuyển đổi từ cách biểu diễn nào đó sang cách biểu diễn khác không có gì khó khăn Nhưng đối với thuật toán này thì làm trên ma trận kề ngắn gọn hơn, đối với thuật toán kia có thể làm trên danh sách cạnh dễ dàng hơn v.v
Trang 221.3 Giới thiệu một số bài toán trên đồ thị
1.3.1 Bài toán tìm kiếm
Một bài toán quan trọng trong lí thuyết đồ thị là bài toán duyệt tất cả các đỉnh có thể đến được từ một đỉnh xuất phát nào đó Vấn đề này đưa về một bài toán liệt kê mà yêu cầu của nó là không được bỏ sót hay lặp lại bất kỳ đỉnh nào Chính vì vậy mà ta phải xây dựng những thuật toán cho phép duyệt một cách hệ thống các đỉnh, những thuật toán như vậy gọi là thuật toán tìm
kiếm trên đồ thị Ta quan tâm đến hai thuật toán cơ bản nhất: thuật toán tìm
kiếm theo chiều sâu và thuật toán tìm kiếm theo chiều rộng
Giới thiệu thuật toán tìm kiếm DFS
Tư tưởng của thuật toán có thể trình bày như sau: Ta sẽ bắt đầu tìm kiếm từ một đỉnh v0 nào đó của đồ thị Sau đó chọn u là một đỉnh tuỳ ý kề với
0
v và lặp lại quá trình đối với u Ở bước tổng quát, giả sử ta đang xét đỉnh v Nếu như trong số các lặp lại bất kỳ đỉnh nào Vì vậy, cần phải xây dựng những thuật toán cho phép đỉnh kề với v tìm được đỉnh w là chưa được xét thì
ta sẽ xét đỉnh này (nó sẽ trở thành đã xét) và bắt đầu từ nó ta tiếp tục quá trình tìm kiếm Còn nếu như không còn đỉnh nào kề với v là chưa xét thì ta nói rằng đỉnh này đã duyệt xong và quay trở lại tiếp tục tìm kiếm từ đỉnh mà trước đó ta đến được từ đỉnh v (nếu vv0 thì kết thúc tìm kiếm)
Có thể nói nôm na là tìm kiếm theo chiều sâu, bắt đầu từ đỉnh v được thực hiện trên cơ sở tìm kiếm theo chiều sâu từ tất cả các đỉnh chưa xét kề với
v Quá trình này có thể mô tả bởi thủ tục đệ qui sau đây:
Trang 23end; (*đỉnh v đã duyệt xong*)
Trong quá trình cài đặt chương trình, chúng tôi không sử dụng thủ tục
Trang 241 (1) 1 2 (1,2) Tiến sâu xuống thăm 2
2 (1,2) 2 3 (1,2,3) Tiến sâu xuống thăm 3
3 (1,2,3) 3 5 (1,2,3,5) Tiến sâu xuống thăm 5
4 (1,2,3,5) 5 Không có (1,2,3) Lùi lại
5 (1,2,3) 3 Không có (1,2) Lùi lại
6 (1,2) 2 4 (1,2,4) Tiến sâu xuống thăm 4
7 (1,2,4) 4 6 (1,2,4,6) Tiến sâu xuống thăm 6
8 (1,2,4,6) 6 Không có (1,2,4) Lùi lại
9 (1,2,4) 4 Không có (1,2) Lùi lại
11 (1) 1 Không có Lùi hết dây chuyền, Xong
Hình 1.10 Minh hoạ ví dụ về thuật toán DFS dạng bảng
Rõ ràng lệnh gọi DFS v sẽ cho phép đến thăm tất cả các đỉnh thuộc cùng thành phần liên thông với đỉnh v, bởi vì sau khi thăm đỉnh là lệnh gọi đến
thủ tục DFS đối với tất cả các đỉnh kề với nó Mặt khác, do mỗi khi thăm đỉnh v
xong, biến chuaxet v được đặt lại giá trị false nên mỗi đỉnh sẽ được thăm
đúng một lần Thuật toán lần lượt sẽ tiến hành tìm kiếm từ các đỉnh chưa được thăm, vì vậy nó sẽ xét qua tất cả các đỉnh của đồ thị (không nhất thiết phải là liên thông)
Giới thiệu thuật toán tìm kiếm BFS
Cơ sở của phương pháp cài đặt này là “lập lịch” duyệt các đỉnh Việc thăm một đỉnh sẽ lên lịch duyệt các đỉnh kề với nó sao cho thứ tự duyệt là ưu
tiên chiều rộng (đỉnh nào gần S hơn sẽ được duyệt trước)
Giả sử ta có một danh sách chứa những đỉnh đang “chờ” thăm Tại mỗi bước ta thăm một đỉnh đầu danh sách và cho những đỉnh chưa “xếp hàng” kề với nó xếp hàng thêm vào cuối danh sách Chính vì nguyên tắc đó nên danh
Trang 25sách chứa những đỉnh đang chờ sẽ được tổ chức dưới dạng hàng đợi
(QUEUE)
Để ý rằng trong thuật toán tìm kiếm theo chiều sâu đỉnh được thăm càng muộn sẽ càng sớm trở thành đã duyệt xong Điều đó là hệ quả tất yếu
của các đỉnh được thăm sẽ được kết nạp vào trong ngăn xếp (STACK) Tìm
kiếm theo chiều rộng trên đồ thị, nếu nói một cách ngắn gọn, được xây dựng
trên cơ sở thay thế ngăn xếp bởi hàng đợi (QUEUE) Với sự cải biên như
vậy, đỉnh được thăm càng sớm, sẽ càng sớm trở thành đã duyệt xong (tức là càng sớm rời khỏi hàng đợi) Một đỉnh sẽ trở thành đã duyệt xong ngay sau khi ta xét xong tất cả các đỉnh kề (chưa được thăm) với nó Thủ tục có thể mô
tả như sau:
Procedure BFS( v );
Begin
QUEUE:=; Put( v ); (*Kết nạp v vào QUEUE *) Chuaxet[ v ]:=false;
chuaxet[u]:=false;
end;
end;
end;
Trang 26Khi đó, thuật toán tìm kiếm theo chiều rộng trên đồ thị được thực hiện nhờ thuật toán sau:
Hình 1.11 Minh họa ví dụ về thuật toán BFS dạng bảng
1.3.2 Bài toán tìm luồng lớn nhất trong mạng vận tải
Mạng vận tải được định nghĩa là bộ GV E w s t, , , , , ở đây V E, là một đồ thị có hướng liên thông không có khuyên,
w ER r R r là hàm trọng lượng trên tập cung E, s là một đỉnh xác định cho trước gọi là đỉnh phát, còn t là một đỉnh xác định cho trước khác gọi là đỉnh thu
Định lý Ford – Fulkerson về luồng lớn nhất: Trong mọi mạng vận
tải GV E w s t, , , , , giá trị luồng lớn nhất trong mạng bằng khả năng thông
qua của lát cắt nhỏ nhất và tách được s và t của nó