Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 43

Tìm kiếm trên đồ thị

DFS và BFS
Ý tưởng
• Ý tưởng cơ bản: quét qua tất cả các đỉnh trong đồ
thị (vét cạn)
• Câu hỏi 1: để tăng tốc, làm thế nào để chỉ xét mỗi
đỉnh đúng 1 lần (không lặp lại)
• Câu hỏi 2: làm sao xác định index của đỉnh kế tiếp
cần duyệt?
Ý tưởng
• Câu hỏi 1: để tăng tốc, làm thế nào để chỉ xét mỗi
đỉnh đúng 1 lần (không lặp lại)
• -> Sử dụng một mảng chuaxet[] để lưu xem đỉnh đã
được xét hay chưa
• Câu hỏi 2: làm sao xác định index của đỉnh kế tiếp
cần duyệt?
• -> Sử dụng một mảng để lưu index của các đỉnh
mục tiêu kế tiếp
Ý tưởng
• Câu hỏi kế tiếp: chọn index đỉnh đưa vào mảng mục
tiêu như thế nào
• 1. Phải là đỉnh có đặc điểm gì?
• 2. Mảng mục tiêu có thuộc tính gì (FILO hay FIFO)?
Ý tưởng
• Câu hỏi kế tiếp: chọn index đỉnh để đưa vào mảng
mục tiêu như thế nào
• 1. Phải là đỉnh chưa xét (dùng mảng chuaxet[]).
Dùng index của các đỉnh liền kề với đỉnh đang xét
(đơn giản, trực tiếp)
• 2. Tùy vào kiểu của mảng mục tiêu (FIFO hay FILO)
mà behavior của thuật toán sẽ khác nhau.
• FIFO: “breadth-first” đi hết xung quanh rồi mới
xuống sâu hơn..
• FILO: “depth-first” xuống sâu nhất có thể rồi mới đi
xung quanh
• Behavior còn phụ thuộc vào thứ tự đỉnh đưa vào
stack hoặc queue
• Lưu ý: Nhiều trường hợp duyệt không chỉ đi qua
đỉnh, mà thường làm vài thao tác trên đỉnh. Tùy vị
trí hàm thao tác mà thứ tự sẽ thay đổi
Mã giả ví dụ

Depth-first search Breadth-first search


• DFS1(G, S, root) • BFS1(G, Q, root)
ghi nhận root là đã xét ghi nhận root là đã xét
in giá trị tại root in giá trị tại root
Tìm tập đỉnh kề chưa xét V Tìm tập đỉnh kề chưa xét V
Đưa V vào S (push) Đưa V vào Q (enqueue)

nếu S không rỗng nếu Q không rỗng


Lấy đỉnh đầu v trong S (pop) Lấy đỉnh đầu v trong Q
DFS1(G,S,v) (dequeue)
BFS1(G,Q,v)
Ví dụ: DFS(A)
TT Đã xét Chưa xét Stack
0 A B,C,D,E,F [B,C]
1 A,C B,D,E,F [B,E,F]
2 A,C,F B,D,E [B,E]
3 A,C,F,E B,D [B]
4 A,C,F,E,B D [D]
5 A,C,F,E,B,D []
Ví dụ: BFS(A)
TT Đã xét Chưa xét Queue
0 A B,C,D,E,F [B,C]
1 A,B C,D,E,F [C,D]
2 A,B,C D,E,F [D,E,F]
3 A,B,C,D E,F [E,F]
4 A,B,C,D,E F [F]
5 A,B,C,D,E,F []
Mã giả ví dụ

Depth-first search Breadth-first search


• DFS2(G, S, root) • BFS2(G, Q, root)
ghi nhận root là đã xét ghi nhận root là đã xét
Tìm tập đỉnh kề chưa xét V Tìm tập đỉnh kề chưa xét V
Đưa V vào S (push) Đưa V vào Q (enqueue)

nếu S không rỗng nếu Q không rỗng


Lấy đỉnh đầu v trong S (pop) Lấy đỉnh đầu v trong Q
DFS2(G,S,v) (dequeue)
in giá trị tại root BFS2(G,Q,v)
in giá trị tại root
Ví dụ:
• Kết quả khi chạy trên cùng 1 đồ thị như sau
• BFS1: ABCDEF
• BFS2: FEDCBA
• DFS1: ACFEBD
• DFS2: DBEFCA
Tràn bộ đệm (stack overflow)
• Là tình huống sử dụng hết dung lượng của callstack
• 2 kiểu thường gặp:
• Đệ quy quá nhiều
• Biến quá lớn
• Ảnh hưởng chương trình, nguy cơ bảo mật
Khử đệ quy

• Xuli1dinh(G,S,root) • Xuli1dinh(G,Q,root)
ghi nhận root là đã xét ghi nhận root là đã xét
Tìm tập đỉnh kề chưa xét V Tìm tập đỉnh kề chưa xét V
Đưa V vào S (push) Đưa V vào Q (enqueue)
nrBFS(G,Q,root)
nrDFS(G,S,root)
Xuli1dinh(G,Q,root)
Xuli1dinh(G,S,root)
while Q không rỗng
while S không rỗng
Lấy đỉnh đầu chưa xét v trong
Lấy đỉnh đầu chưa xét v trong Q (dequeue đến khi có đỉnh chưa
S (pop đến khi có đỉnh chưa xét) xét)
Xuli1dinh(G,S,v) Xuli1dinh(G,Q,v)
DFS
BFS
Độ phức tạp
• Tùy theo phương pháp biểu diễn đồ thị
• Biểu diễn bằng ma trận kề với n đỉnh: O(n2)
• Biểu diễn bằng danh sách cạnh với n đỉnh, m cạnh:
O(n.m)
• Biểu diễn bằng danh sách kề với n đỉnh, m cạnh:
O(max(n,m))
Độ phức tạp
• Thường thì số cạnh lớn hơn số đỉnh (m>n), khi đó
danh sách cạnh hơi thua kém ma trận kề. Danh
sách kề thì vượt trội hơn hẳn
Các ứng dụng cơ bản của DFS và
BFS
• Duyệt tất cả thành phần liên thông của đồ thị
• Tìm đường đi từ đỉnh s đến đỉnh t trên đồ thị
• Tìm các đỉnh trụ trên đồ thị vô hướng
• Tìm các cạnh cầu trên đồ thị vô hướng
• Xác định tính liên thông mạnh trên đồ thị có hướng
• Xác định tính liên thông yếu trên đồ thị có hướng
Xác định các thành phần liên
thông
• Bài toán: cho đồ thị vô hướng G=<V,E>. Xác định
các thành phần liên thông của G
Xác định các thành phần liên
thông
• Bài toán: cho đồ thị vô hướng G=<V,E>. Xác định
các thành phần liên thông của G
• Thuật toán:
• Khởi tạo với số thành phần liên thông là 0, tất cả đỉnh
đều chưa xét
• Lặp lại trên tập đỉnh chưa xét:
• Nếu tập đỉnh chưa xét không rỗng:
• Lấy một đỉnh chưa xét u
• Tăng số thành phần liên thông thêm 1
• Duyệt DBFS(u) và ghi chú lại các đỉnh đã xét
• In kết quả
Ví dụ
Ví dụ
• 2 thành phần liên thông
• BFS(1) =
• {1,3,5,7,9,11,13}
• BFS(2) =
• {2,4,6,8,10,12}
Tìm đường đi giữa 2 đỉnh
• Bài toán: Cho đồ thị G=<V,E> (vô hướng hoặc có
hướng). Tìm một đường đi từ đỉnh s đến đỉnh t
(không cần phải ngắn nhất)
Tìm đường đi giữa 2 đỉnh
• Bài toán: Cho đồ thị G=<V,E> (vô hướng hoặc có
hướng). Tìm một đường đi từ đỉnh s đến đỉnh t
(không cần phải ngắn nhất)
• Thuật toán:
• Khởi tạo mảng đường đi rỗng (mảng truoc[])
• DFS(s)
• Mỗi khi duyệt 1 đỉnh, ghi lại đỉnh trước đó vào mảng đường đi.
• Nếu có duyệt đến đỉnh t thì ngưng: đã tìm được đường
đi
• Nếu kết thúc mà không duyệt đến đỉnh t: không có
đường đi từ s đến t
BFS
DFS
Xác định tính liên thông mạnh
• Bài toán: cho đồ thị G=<V,E> có hướng. Xác định
tính liên thông mạnh của G
Xác định tính liên thông mạnh
• Bài toán: cho đồ thị G=<V,E> có hướng. Xác định
tính liên thông mạnh của G
• Thuật toán:
• Lặp lại với mỗi đỉnh u
• Khởi tạo mảng chưa xét
• DBFS(u)
• Nếu mảng chưa xét không rỗng
• Trả về “không liên thông mạnh” (đỉnh u không đi được hết)
• Trả về “liên thông mạnh” (mọi đỉnh đều đi được hết)
Tìm các đỉnh trụ
• Bài toán: cho đồ thị G=<V,E>, tìm các đỉnh trụ của G
• Đỉnh trụ: nếu xóa đỉnh đó và các cạnh kề, số thành
phần liên thông tăng lên
Tìm các đỉnh trụ
• Bài toán: cho đồ thị G=<V,E>, tìm các đỉnh trụ của G
• Thuật toán:
• Tính số thành phần liên thông (thuật toán tìm số thành
phần liên thông bằng DBFS ở trên)
• Lặp lại với mỗi đỉnh u:
• Khởi tạo mảng chưa xét (nhưng đặt đỉnh u là đã xét)
• Tính số thành phần liên thông
• Nếu số thành phần liên thông tăng lên so với ban đầu: u là đỉnh
trụ
Tìm các cạnh cầu
• Bài toán: cho đồ thị G=<V,E>, tìm các cạnh cầu của
G
Tìm các cạnh cầu
• Bài toán: cho đồ thị G=<V,E>, tìm các cạnh cầu của
G
• Thuật toán:
• Tính số thành phần liên thông của G
• Lặp lại với mỗi cạnh e:
• Tạo đồ thị G’ bằng cách bỏ cạnh e khỏi G
• Tính số thành phần liên thông của G’
• Nếu số thành phần liên thông tăng lên, cạnh e là cạnh cầu của
G
Định chiều đồ thị
• Bài toán: cho đồ thị G=<V,E> vô hướng. Xác định
xem G có định chiều được hay không?
Định chiều đồ thị
• Bài toán: cho đồ thị G=<V,E> vô hướng. Xác định
xem G có định chiều được hay không?
• Định lí: đồ thị vô hướng G định chiều được khi và
chỉ khi tất cả cạnh không phải cạnh cầu
• Thuật toán:
• Tìm các cạnh cầu của G (bằng thuật toán tìm cạnh cầu ở
trên)
• Nếu số cạnh cầu bằng 0, G định chiều được, nếu lớn hơn
0 thì G không định chiều được
Một số ứng dụng khác
• DFS
• Đặt N quân hậu lên bàn cờ
• Phát hiện loop trong đồ thị
• Giải mê cung
• BFS
• Tìm đường đi ngắn nhất (đồ thị không trọng số)
• Cào dữ liệu ở Internet
• Chess: Tính toán nước đi
• Xử lý từng tầng dữ liệu nhiều tầng (file system, HTML…)
• Tại sao DFS phù hợp tình huống bài toán đặt hậu và
BFS phù hợp tình huống tính toán nước cờ?
• Trong tình huống bài toán đặt hậu, ta thường quan
tâm tìm 1 lời giải cụ thể thỏa yêu cầu. Do đó ta
muốn xuống sâu nhất càng sớm càng tốt
• Nếu yêu cầu là tìm mọi lời giải, DFS không vượt trội so
với BFS
• Trong tình huống tính toán nước cờ, kích thước cây
trạng thái quá sâu, không thể xuống sâu và so sánh
hiệu quả. BFS tìm kiếm toàn bộ ở tầng nông, so
sánh toàn bộ khả năng trong bao nhiêu nước kế
tiếp
• BFS và DFS vẫn là nghiên về vét cạn, không phải tìm
“thông minh” như tìm kiếm nhị phân
• Vì không giả thiết đồ thị có đặc điểm cấu trúc gì
• Có thể sử dụng rộng rãi
• Không tận dụng được cấu trúc đồ thị nếu có

You might also like