Đi hết một nhánh trước khi đổi nhánh
DFS (Depth First Search) bắt đầu ở s, đánh dấu s rồi xét các đỉnh kề. Gặp đỉnh chưa thăm thì duyệt sâu từ đỉnh đó; khi không còn đỉnh kề chưa thăm, quay lại bước trước để xét nhánh khác.
Cần đánh dấu **trước khi đi tiếp** để cạnh quay về hoặc chu trình không làm duyệt vô hạn. Một lượt DFS từ s chỉ đến các đỉnh có đường đi từ s. Muốn duyệt toàn bộ đồ thị, tiếp tục bắt đầu từ đỉnh chưa thăm.
Truyền mảng đánh dấu vào hàm con
dfs_visit nhận seen và order dùng chung trong một lượt; không phụ thuộc biến toàn cục. dfs tạo lượt mới từ s; dfs_all dùng một seen xuyên suốt các lần bắt đầu.
```python
def check_vertex(Adj, s):
if not 0 <= s < len(Adj):
raise ValueError("Đỉnh nằm ngoài đồ thị")
def dfs_visit(Adj, u, seen, order):
seen[u] = True
order.append(u)
for v in Adj[u]:
if not seen[v]:
dfs_visit(Adj, v, seen, order)
def dfs(Adj, s):
check_vertex(Adj, s)
seen = [False]*len(Adj)
order = []
dfs_visit(Adj, s, seen, order)
return order
def dfs_all(Adj):
seen = [False]*len(Adj)
order = []
for u in range(len(Adj)):
if not seen[u]:
dfs_visit(Adj, u, seen, order)
return order
```
DFS mẫu bắt đầu từ 0
Các hàng Adj của mẫu được xét tăng dần. Thứ tự phát hiện là **0,1,3,5,4,2**.
|Đang ở|Việc tiếp theo|
|---|---|
|0|Chọn 1 trước 2|
|1|Bỏ qua 0 đã thăm, đi 3|
|3|Đi 5|
|5|Đi 4|
|4|Bỏ qua 1, đi 2|
|2|0 và 4 đã thăm; quay lại|
Đỉnh 6 không xuất hiện vì biệt lập. dfs_all thêm 6 sau lượt này. Đổi thứ tự xét đỉnh kề có thể đổi thứ tự DFS nhưng không đổi tập đỉnh đi tới được.
Với danh sách kề, duyệt toàn bộ đồ thị có chi phí O(V+E): mỗi đỉnh thăm một lần, mỗi cạnh vô hướng xét hai chiều, hệ số 2 không làm đổi bậc tăng trưởng. Nếu quét n ô của mỗi hàng ma trận để tìm đỉnh kề thì có thể tốn O(V²). Đệ quy của mẫu dùng đồ thị nhỏ.
Bài luyện
Theo Adj mẫu tăng dần, thứ tự DFS đệ quy từ 0 là gì?
Đây là thứ tự BFS của mẫu.
Đây là thứ tự bản stack ở bài sau.
Đúng. Đi sâu theo nhánh đầu tiên chưa thăm.
Một lượt từ 0 không tới đỉnh 6 biệt lập.
Lời giải
Từ 0 chọn 1, từ 1 chọn 3, từ 3 chọn 5, từ 5 chọn 4, từ 4 chọn 2. Các nhánh còn lại đều đã thăm, nên kết quả là 0,1,3,5,4,2.
Bài luyện
Vì sao đánh dấu đỉnh trước khi gọi DFS sang các đỉnh kề?
Không xóa cạnh, chỉ ghi trạng thái.
Đúng. Đánh dấu ngăn thăm lại.
DFS không bảo đảm đường ít cạnh nhất.
Không có bước sắp ma trận.
Lời giải
Đồ thị có thể có chu trình hoặc cạnh vô hướng đi ngược về đỉnh trước. Nếu đã đánh dấu, kiểm seen[v] giúp không gọi lại DFS ở đỉnh đó, giữ mỗi đỉnh được phát hiện một lần.
Bài luyện
dfs_all cần xử lí thêm 6 sau lượt từ 0 vì lý do nào?
Đỉnh nhỏ nhất là 0.
Duyệt đỉnh không phải bài toán đi qua mỗi cầu đúng một lần.
Thứ tự DFS không nhất thiết tăng.
Đúng. Vòng ngoài bao phủ phần chưa tới được.
Lời giải
Lượt DFS từ 0 chỉ đánh dấu nhóm 0 đến 5. Khi vòng ngoài tới 6, seen[6] vẫn False nên bắt đầu một lượt mới để thăm đỉnh biệt lập này.
Đang học