Duyệt theo chiều sâu¶
Tóm lược nội dung
Bài này trình bày thuật toán duyệt đồ thị theo chiều sâu (DFS) và cách cài đặt bằng ngôn ngữ Python.
Khái quát¶
Duyệt đồ thị theo chiều sâu, gọi tắt là DFS, là thuật toán duyệt đồ thị:
Hoạt động theo nguyên tắc đi sâu nhất có thể theo mỗi nhánh trước khi quay lui.
- Bắt đầu từ một đỉnh bất kỳ, tạm gọi là đỉnh
start
. - Sau đó ghé thăm một trong các đỉnh kề của
start
, tạm gọi là đỉnhu
. - Tiếp tục ghé thăm một trong các đỉnh kề của
u
, tạm gọi là đỉnhv
. - Lặp lại quá trình trên cho đến khi không còn đỉnh kề nào chưa ghé thăm.
- Quay lui để ghé thăm các đỉnh kề khác.
Nói cách khác, DFS ưu tiên duyệt các đỉnh ở xa nhất trước, sau đó mới đến các đỉnh ở gần hơn.
Ứng dụng¶
DFS được áp dụng phổ biến cho các bài toán như:
- Tìm kiếm đường đi trong mê cung.
- Kiểm tra tính liên thông của đồ thị.
- Tìm kiếm bạn bè trên mạng xã hội.
- Tô màu đồ thị.
Các bước thực hiện¶
Để thực hiện thuật toán DFS, ta có thể sử dụng kỹ thuật đệ quy hoặc cấu trúc ngăn xếp.
Bài học này chỉ đề cập cách sử dụng ngăn xếp.
Các bước thực hiện như sau:
Buớc 1: Đưa đỉnh start
vào ngăn xếp.
Bước 2: Lặp lại các thác tác sau cho đến khi ngăn xếp không còn phần tử:
- Lấy đỉnh trên cùng ra khỏi ngăn xếp, đặt là đỉnh
current
. -
Duyệt các đỉnh
v
kề với đỉnhcurrent
vừa lấy ra:Nếu
v
chưa được ghé thăm thì: - Đánh dấu đỉnhv
đã ghé thăm. - Đưa đỉnhv
vào ngăn xếp. - Thực hiện các công việc cần thiết với đỉnhv
.
Ví dụ:
Gọi Thành phố Hồ Chí Minh là đỉnh start
.
Theo DFS, sau khi duyệt đỉnh start
, ta sẽ duyệt đỉnh đầu tiên kề với đỉnh start
, giả sử là 'Đồng Nai'
.
Ứng với đỉnh 'Đồng Nai'
, ta sẽ duyệt đỉnh đầu tiên kề với Đồng Nai
, giả sử là 'Bình Phước'
.
Ứng với đỉnh 'Bình Phước'
, ta sẽ duyệt đỉnh đầu tiên kề với Bình Phước
, giả sử là 'Lâm Đồng'
.
Cứ như thế cho đến khi đến được đỉnh cuối cùng hoặc không còn đỉnh kề nào chưa ghé thăm.
Ngoài ra, để tránh lặp lại, ta cần đánh dấu các đỉnh đã ghé thăm.
Bài toán minh hoạ¶
Yêu cầu¶
Tìm đường đi giữa hai đỉnh trong đồ thị bằng cách áp dụng DFS.
Input¶
Output¶
Giải thích¶
Input:
- Dòng đầu tiên gồm các đỉnh của đồ thị.
- Các dòng tiếp theo mô tả các cạnh của đồ thị.
- Hai dòng cuối cùng là hai đỉnh cần tìm đường đi.
Đồ thị có thể được phác hoạ như sau:
Output:
Đường đi từ đỉnh 0
đến đỉnh 5
là: 0 -> 2 -> 4 -> 5
Cách giải đề xuất¶
Khai báo biến chứa input¶
Khai báo biến data
chứa dữ liệu đầu vào như bài trước.
Khởi tạo danh sách kề¶
Viết hàm init_adjacency_list()
để khởi tạo danh sách kề từ input như bài trước.
Khởi tạo cho hàm DFS¶
Hàm dfs()
dùng để tìm đường đi giữa hai đỉnh trong đồ thị bằng DFS, gồm ba tham số:
adj_list
: danh sách kề của đồ thị.start
: đỉnh bắt đầu.finish
: đỉnh kết thúc.
Hàm này trả về mảng trace
dùng để truy vết đường đi. Nếu không có đường đi, hàm trả về mảng rỗng hoặc None
.
Trước khi thực hiện duyệt DFS, ta cần khởi tạo các biến sau:
-
stack
: ngăn xếp dùng để lưu các đỉnh trong khi duyệt DFS.Ban đầu, ngăn xếp chứa đỉnh
start
. -
Thay vì dùng biến
visited
, ta dùng biếntrace
để vừa đánh dấu các đỉnh đã ghé thăm, vừa lưu vết nhằm truy ngược đường đi sau này.Cụ thể,
trace[v] = u
nghĩa là trước đỉnhv
là đỉnhu
.Ví dụ:
trace[start] = None
nghĩa là đỉnhstart
không có đỉnh nào nằm trước nó.trace[5] = 3
nghĩa là trước đỉnh5
là đỉnh3
.Biến
trace
vừa dùng để đánh dấu các đỉnh đã ghé thăm, vừa dùng để truy vết đường đi.Biến
trace
có thể là kiểulist
hoặcdictionary
. Trong bài này, ta sử dụng kiểudictionary
để tiện cho việc lưu trữ và truy xuất.
Thực hiện DFS¶
Buớc 1: Đưa đỉnh start
vào ngăn xếp.
Đã thực hiện ở bước khởi tạo.
Bước 2:
Duyệt ngăn xếp cho đến khi ngăn xếp không còn phần tử nào nữa:
- Lấy đỉnh trên cùng ra khỏi ngăn xếp, đặt là đỉnh
current
. -
Duyệt các đỉnh
v
kề với đỉnhcurrent
vừa lấy ra:Nếu
v
chưa được ghé thăm thì: - Đánh dấu đỉnhv
bằng mảngtrace
:trace[v] = current
. - Đưa đỉnhv
vào ngăn xếp.
Truy vết đường đi¶
Hàm get_path()
dùng để truy vết đường đi dựa trên mảng trace
.
Cụ thể, hàm này dùng biến tạm node
để di chuyển ngược từ đỉnh finish
về đỉnh trước đó, dựa trên trace[finish]
, và dần dần về đỉnh start
.
Hàm này trả về mảng path
chứa đường đi.
In kết quả¶
Trong chương trình chính, ta gọi các hàm vừa viết và in kết quả ra màn hình.
Nhận xét¶
-
DFS không tìm ra đường đi ngắn nhất, mà chỉ tìm đường liên thông, nghĩa là có đường đi hoặc không có đường đi từ đỉnh này sang đỉnh khác.
-
Có thể có nhiều đường đi, nhưng DFS luôn trả về đường đi có thứ tự từ điển nhỏ nhất.
Mã nguồn¶
Code đầy đủ được đặt tại:
Some English words¶
Vietnamese | Tiếng Anh |
---|---|
duyệt theo chiều sâu | depth first search (DFS) |