TIỂU LUẬN MÔN HỌC PHƯƠNG PHÁP TOÁN CHO TIN HỌC
Trang 1BỘ GIÁO DỤC VÀ ĐÀO TẠO
-TIỂU LUẬN MÔN HỌC PHƯƠNG PHÁP TOÁN CHO TIN HỌC
Đề tài:
TÌM HIỂU VÀ TÍNH ĐỘ PHỨC TẠP
CỦA THUẬT TOÁN DFS ( Depth First Search)
TP HCM, tháng 9/2013
Trang 2MỤC LỤC
I MÔ TẢ THUẬT TOÁN DFS: 2
1 Giới thiệu thuật toán DFS: 2
1.1 DFS là gì ? 2
1.2 Ý tưởng của DFS: 2
1.3 Ví dụ về cách thực hiện của thuật toán DFS: 2
2 Ứng dụng của DFS: 8
3 Độ phức tạp của thuật toán DFS: 9
3.1 Mã giả: 9
3.2 Các lệnh thực hiện: 9
3.3 Trường hợp tốt nhất: 10
3.4 Trường hợp xấu nhất: 10
4 Cài đặt (C#): 10
5 Tính đúng đắng của thuật toán: 11
II MÔ TẢ THUẬT TOÁN BFS: 12
1 Giới thiệu thuật toán BFS: 12
1.1 BFS là gì ? 12
1.2 Ý tưởng của BFS: 12
1.3 Ví dụ về cách thực hiện của thuật toán BFS: 12
2 Ứng dụng của thuật toán BFS: 17
3 Độ phức tạp của thuật toán BFS: 17
III SO SÁNH DFS VÀ BFS: 18
TÀI LIỆU THAM KHẢO: 18
1
Trang 3I MÔ TẢ THUẬT TOÁN DFS:
1 Giới thiệu thuật toán DFS:
1.1 DFS là gì ?
Thuật toán DFS (Depth-first search – Duyệt theo chiều sâu) là thuật toán duyệt (tìm kiếm) trên cây hoặc đồ thị
1.2 Ý tưởng của DFS:
Khởi đầu tại một nút gốc (hoặc một nút nào đó coi như gốc) và duyệt sâu theo một nhánh con của nút góc (1) Khi không còn nút con để duyệt ta quay lui trở về nút cha (2), tiếp tục duyệt sâu các nút con còn lại của nút cha và lập lại cho tới khi tìm được kết quả hoặc hết nút để duyệt
*Chú thích:
(1) Chọn nút con trong nhiều nút con phụ thuộc vào cấu trúc cài đặt của đồ thị đưa vào Việc lựa chọn nút con nào sẽ ảnh hưởng đến thứ tự duyệt
và cũng ảnh hưởng đến độ phức tạp của thuật toán.
(2) Để quay lui được thì khi duyệt một nút có nhánh con ta phải lưu vào 1 ngăn xếp LIFO (Last in fist out – vào sau ra trước) Khi quay lui ta xác định nút cha bằng cách lấy nút vừa thêm vào ngăn sau cùng
1.3 Ví dụ về cách thực hiện của thuật toán DFS:
Cho cây như sau:
Trang 4Bước 1: đưa nút góc là [1] vào ngăn xếp và bắt đầu duyệt cây.
Bước 2: Duyệt [1] (vì [1] là nút thêm sau cùng vào ngăn xếp) Đưa tất cả các nút con của [1] vào ngăn xếp và đánh dấu đã duyệt [1]
3
Trang 5Bước 3: duyệt [4] (vì [4] là nút thêm sau cùng vào ngăn xếp) Đưa tất cả các nút con của [4] vào ngăn xếp và đánh dấu đã duyệt [4]
Bước 4: duyệt [7] (vì [7] là nút thêm sau cùng vào ngăn xếp) Đưa tất cả các nút con của [7] vào ngăn xếp và đánh dấu đã duyệt [7]
Trang 6Bước 5: duyệt [8] (vì [8] là nút thêm sau cùng vào ngăn xếp) Đánh dấu đã duyệt [8] Do [8] không có nút con nên ta thực hiện quay lui bằng cách xoá [8] ra khổi ngăn xếp
Bước 6: duyệt [7] (vì [7] là nút thêm sau cùng vào ngăn xếp) Do [7] đã được duyệt rồi nên ta chỉ xoá [7] khổi ngăn xếp
Bước 7: duyệt [4] (vì [4] là nút thêm sau cùng vào ngăn xếp) Do [4] đã được duyệt rồi nên ta chỉ xoá [4] khổi ngăn xếp
5
Trang 7Bước 8: duyệt [3] (vì [3] là nút thêm sau cùng vào ngăn xếp) Đưa tất cả các nút con của [3] vào ngăn xếp và đánh dấu đã duyệt [3]
Bước 9: duyệt [6] (vì [6] là nút thêm sau cùng vào ngăn xếp) Đánh dấu đã duyệt 6 Do 6 không có nút con nên ta thực hiện quay lui bằng cách xoá [6] ra khổi ngăn xếp
Trang 8Bước 10: duyệt [3] (vì [3] là nút thêm sau cùng vào ngăn xếp) Do [3] đã được duyệt rồi nên ta chỉ xoá [3] khổi ngăn xếp
Bước 11: duyệt [2] (vì [2] là nút thêm sau cùng vào ngăn xếp) Đưa tất cả các nút con của [2] vào ngăn xếp và đánh dấu đã duyệt [2]
7
Trang 9Bước 12: duyệt [5] (vì [5] là nút thêm sau cùng vào ngăn xếp) Đánh dấu đã duyệt [5] Do [5] không có nút con nên ta thực hiện quay lui bằng cách xoá [5] ra khổi ngăn xếp
Bước 13: duyệt [2] (vì [2] là nút thêm sau cùng vào ngăn xếp) Do [2] đã được duyệt rồi nên ta chỉ xoá [2] khổi ngăn xếp
Bước 14: duyệt [1] (vì [1] là nút thêm sau cùng vào ngăn xếp) Do [1] đã được duyệt rồi nên ta chỉ xoá [1] khổi ngăn xếp
Bước 15: dừng lại vì ngăn xếp không còn nút (có thể nói là không còn nút chưa được duyệt)
2 Ứng dụng của DFS:
-Xác định các thành phần liên thông của đồ thị: DFS duyệt tất cả các đỉnh có cùng thành phần liên thông Nên số thành phần liên thông của đồ thị chính là số lần DFS Ta sẽ dùng thêm biến đếm để đếm số thành phần liên thông
-Bài toán tìm đường đi giữa hai đỉnh của đồ thị: DFS duyệt lần lượt các đỉnh của
đồ thị nên khi duyệt ta cùng gập 2 định cần xác định thì giữa 2 đỉnh trên có tồn tại đường đi Vấn đề còn lại của bài toán là: Nếu tồn tại đường đi nối đỉnh s và đỉnh t thì làm cách nào để viết được hành trình (thứ tự các đỉnh)
-Sắp xếp tô-pô cho đồ thị
-Xác định các thành phần liên thông mạnh của đồ thị có hướng
Trang 10-Kiểm tra một đồ thị có phải là đồ thị phẳng hay không
3 Độ phức tạp của thuật toán DFS:
3.1 Mã giả:
Input: Goc, NganXep;
2.2.3 Foreach (var Children in Nut.Children) ≤ n-1
2.2.3.1 NganXep.Push(Children); 1
EndForeach 2.2.4 NganXepKetQua.Push(Nut); 1
2.2.5 Nut.TrangThai = true; 1
EndIF End;
Output: NganXepKetQua;
3.2 Các lệnh thực hiện:
Xét đồ thị có n nút Số phép toán thực hiện ở các dòng lệnh là:
-Dòng lệnh 1: có 1 phép toán.
-Dòng lệnh 2: lệnh while thực hiện tổng cộng 2n lần n lần duyệt tiến và n lần
để quay lui
-Dòng lệnh 2.1: có 1 phép toán.
-Dòng lệnh 2.2: Hàm if chia giải thuật thành 2 giai đoạn Giai đoạn duyệt gồm
n lần và giai đoạn quay lui n lần Mỗi lần lập while hàm if chạy 1 lần
-Dòng lệnh 2.2.1: có 1 phép toán.
-Dòng lệnh 2.2.3: mỗi lần lập của while thì dòng lệnh Foreach có thể lập từ 0
cho đến ≤ n-1 Do đây là vòng lập để đưa các nút con của nút cha vào ngăn xếp do đó
ta có trường hợp tốt nhất là không có nút con và Foreach có 0 lần thực hiện Trường
9
Trang 11hợp xấu là Foreach lập n-1 lần tức là tất cả các nút còn lại đều là con của nút cha đang
xét
-Dòng lệnh 2.2.3.1: có 1 phép toán.
-Dòng lệnh 2.2.4: có 1 phép toán.
-Dòng lệnh 2.2.5: có 1 phép toán.
*Vậy tổng phép toán thực hiện là:
T(n) = 1 + 2n [ 1 + max( 1 , n – 1 + 1 + 1 ) ] = 1 + 2n.(n + 2) = 2n2 + 4n + 1
3.3 Trường hợp tốt nhất:
Khi nút cần tìm là nút góc đầu tiên
3.4 Trường hợp xấu nhất:
Ta thấy trường hợp xấu nhất xảy ra phụ thuộc vào 2 yếu tố:
-Nút cần tìm là nút không có nút con -Việc lựa chọn các nút con để duyệt khiến cho nút cần tìm rơi vào vị trí duyệt cuối cùng của thuật toán
Trong trường hợp này độ phức tạp thuật toán là:
4 Cài đặt (C#):
-Qui ước: Ngăn sếp với các phần từ như sau [ A1, A2….An]
+ A1 được thêm vào đầu tiên thì gọi đầu ngăn sếp là phần hướng về bên trái + An được thêm vào cuối cùng thì gọi cuối ngăn sếp là phần hướng về bên phải
private static Stack<Node> DFS(Node Goc)
{
var NganXep = new Stack<Node>();
//Ngăn xếp xử lý duyệt
var NganXepKetQua = new Stack<Node>();
//Ngăn xếp chứa kết quả
NganXep.Push(Goc);
//Đặt nút góc vào Ngăn xếp
Vậy độ phức tạp của thuật toán DFS là O(n2)
O(1)
O(n2)
Trang 12while (NganXep.Count > 0)
//Lập cho đến khi ngăn xếp không còn nút (duyệt tất cả nút)
{
var Nut = NganXep.Peek();
//Lấy nút cuối của ngăn xếp
if (Nut.TrangThai)
//Kiểm tra nút này đã được chưa
{
NganXep.Pop();
//Khi nút đã được duyệt (trạng thái là true) thì xoá khổi ngăn xếp
} else {
foreach (var l in Nut.Children.ToArray().Reverse()) {
NganXep.Push(l);
}
//Lưu tất cả các nút con của nút đang xét vào ngăn xếp
NganXepKetQua.Push(Nut);
//Lưu kết quả để trả về
Nut.TrangThai = true;
//Đánh dấu đã duyệt
} }
return NganXepKetQua;
}
5 Tính đúng đắng của thuật toán:
DFS là thuật toán duyệt cây (đồ thị) do đó thuật toán DFS đúng đắn khi duyệt được qua tất cả các nút (đỉnh) của cây (đồ thị)
Ta có cây G=(V, E) trong đó V là tập hợp các nút, V0 là nút góc, E là tập các cặp gồm 2 nút khác nhau trong đó một nút là nút cha, nút còn lại là nút con của nút cha đó, gọi A là tập các đỉnh đã được duyệt Ta cần chứng minh thuật toán sẽ dẫn đến tập A=V (tức là thuật toán duyệt được tất cả các nút)
Khi DFS chạy sẽ đặt nút góc V0 và các nút con của V0 vào ngăn xếp đồng thời đánh dấu đã duyệt V0 (tức là A = { V0 })
11
Trang 13Lấy một phần tử ở cuối ngăn xếp ra duyệt, nút này được coi là nút góc và thực hiện duyệt như nút V0 Mở rộng ra khi DFS duyệt nút Vn thì tất cả các nút con của Vn
sẽ được đặt vào ngăn xếp để chờ duyệt Do đó DFS có khả năng duyệt được tất cả các nút của cây
II MÔ TẢ THUẬT TOÁN BFS:
1 Giới thiệu thuật toán BFS:
1.1 BFS là gì ?
Thuật toán BFS (Breadth-first search – Duyệt theo chiều rộng) là thuật toán duyệt (tìm kiếm) trên cây hoặc đồ thị
1.2 Ý tưởng của BFS:
Khởi đầu tại một nút gốc (hoặc một nút nào đó coi như gốc) và duyệt tất
cả các nút con kề của nút góc Khi tất cả các nút con kề nút góc đã được duyệt ta tiếp tục chọn một nút con làm góc và lập lại với các nút con cùng cấp (1) với nút này , thực hiện thao tác duyệt như trên cho tới khi tìm được kết quả hoặc hết nút để duyệt
*Chú thích: (1) Để lưu các nút con ta đùng1 hàng đợi FIFO (Fist in fist out – vào trước ra trước) Để lấy nút đang chờ tới lược làm nút góc, ta lấy nút lưu đầu tiền trong hàng đợi
1.3 Ví dụ về cách thực hiện của thuật toán BFS:
Cho cây như sau:
Bước 1: đưa nút góc là [1] vào hàng đợi và bắt đầu duyệt cây
Trang 14Bước 2: Duyệt [1] (vì [1] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [1] vào hàng đợi và đánh dấu đã duyệt [1]
Bước 3: Duyệt [2] (vì [2] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [2] vào hàng đợi và đánh dấu đã duyệt [2]
13
Trang 15Bước 4: Duyệt [3] (vì [3] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [3] vào hàng đợi và đánh dấu đã duyệt [3]
Bước 5: Duyệt [4] (vì [4] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [4] vào hàng đợi và đánh dấu đã duyệt [4]
Trang 16Bước 6: Duyệt [5] (vì [5] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [5] vào hàng đợi và đánh dấu đã duyệt [5]
Bước 7: Duyệt [6] (vì [6] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [6] vào hàng đợi và đánh dấu đã duyệt [6]
15
Trang 17Bước 8: Duyệt [7] (vì [7] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [7] vào hàng đợi và đánh dấu đã duyệt [7]
Bước 9: Duyệt [8] (vì [8] là nút ở đầu ra của hàng đợi) Đưa tất cả các nút con của [8] vào hàng đợi và đánh dấu đã duyệt [8]
Trang 18Bước 10: dừng lại vì hàng đợi không còn nút (có thể nói là không còn nút chưa được duyệt)
2 Ứng dụng của thuật toán BFS:
-Đếm số thành phần liên thông của 1 đồ thị: Ta sẽ dùng BFS duyệt toàn bộ đồ thị từ 1 đỉnh u cho trước Sử dụng một biến đếm để đếm số thành phần liên thông của
đồ thị
-Kiểm tra cạnh cầu của đồ thị: Trước hết ta duyệt BFS 1 lần đồ thị để tìm ra số thành phần liên thông ban đầu.Ta xóa lần lượt các cạnh của đồ thị, sau đó dùng BFS duyệt tìm số thành phần liên thông của đồ thị Nếu số thành phần liên thông tăng chứng
tỏ cạnh bị xóa là cạnh cầu (Cạnh cầu của đồ thị là khi xóa cạnh đó đi thì số thành phần liên thông của đồ thị tăng lên)
-Kiểm tra đỉnh trụ của đồ thị: Trước hết ta duyệt BFS 1 lần đồ thị để tìm ra số thành phần liên thông ban đầu Mỗi lần duyệt ta xóa 1 đỉnh của đồ thị Dùng BFS duyệt tìm số thành phần liên thông của đồ thị Nếu số thành phần liên thông của đồ thị tăng lên thì đỉnh bị xóa là đỉnh trụ (đỉnh trụ của đồ thị là đỉnh mà khi xóa nó đi thì số thành phần liên thông của đồ thị tăng lên)
-Tìm đỉnh thắt của đồ thị
-Đồ thị định chiều
3 Độ phức tạp của thuật toán BFS:
BFS có cùng độ phức tạp với DFS do quá trình duyệt BFS vẫn quét qua tất cả các nút và cũng có thao tác đẩy tất cả các nút con vào hàng đợi
17
Độ phức tạp của thuật toán BFS cũng là O(n2)
Trang 19III SO SÁNH DFS VÀ BFS:
1 Ý tưởng Duyệt theo chiều xâu Duyệt theo chiều rộng
2 Cách lưu trữ Sử dụng ngăn xếp LIFO Sử dụng hàng đợi FIFO
Bộ nhớ Tốn ít do không phải lưu tất
cả các nút ở mỗi cấp
Tốn nhiều do phải lưu tất cả các nút ở mỗi cấp
4 Trường hợp sử
dụng
Duyệt cây có độ sâu (mức) lớn thì hiệu quả hơn
Duyệt cây có độ sâu (mức) nhỏ thì hiệu quả hơn
TÀI LIỆU THAM KHẢO:
-Sách nhập môn Cấu Trúc Dữ Liệu và Giải Thuật - Dương Anh Đức, Đại Học Khoa Học Tự Nhiên
-Sách giải thuật và lập trình – Lê Minh Hoàn, Đại học sư phạm Hà Nội
-Depth-first search (DFS) - http://en.wikipedia.org/wiki/Depth-first_search
-Depth First Search Algorithm - http://www.youtube.com/watch?v=iaBEKo5sM7w
-DFS và ứng dụng - link
-Các thuật toán tìm kiếm trên đồ thị - http://fit.hcmup.edu.vn
-Depth First Traversal in C# - link
-Breadth First Search Algorithm - http://www.youtube.com/watch?v=QRq6p9s8NVg
-BFS và ứng dụng - link