Professional Documents
Culture Documents
Algorithms
Graphs
- Duyệt đồ thị (hay còn gọi là đi qua đồ thị) có nghĩa là ta cần “thăm” tất cả các
đỉnh và cung của đồ thị theo một trật tự nào đó. Giải quyết nhiều vấn đề của lý
thuyết đồ thị đòi hỏi ta cần phải duyệt đồ thị. Vì vậy, các kỹ thuật đi qua đồ thị
đóng vai trò quan trọng trong việc thiết kế các thuật toán đồ thị.
- Chẳng hạn, bằng cách duyệt đồ thị, ta có thể đưa ra thuật giải cho các vấn đề: đồ
thị có chu trình hay không? Đồ thị có liên thông không? Từ đỉnh u bất kỳ ta có thể
đi tới đỉnh v bất kỳ khác hay không?
- Có hai kỹ thuật duyệt đồ thị: duyệt đồ thị theo bề rộng và duyệt đồ thị theo độ
sâu.
1. Tìm kiếm theo chiều sâu (Depth-first search)
- là một kỹ thuật duyệt đồ thị cơ bản. Thuật toán bắt đầu tại một node, và
tiếp tục đi đến các node khác từ node bắt đầu bằng các cạnh của đồ thị.
-Tìm kiếm theo chiều sâu luôn theo một con đường duy nhất trong đồ thị
để tìm các node mới. Sau đó, nó quay lại node liền trước và bắt đầu khai
phá các con đường khác của đồ thị. Thuật toán lưu trữ các node đã duyệt,
để nó đi đến mỗi node một lần duy nhất.
b) Ví dụ
Chúng ta xem xét cách tìm kiếm theo chiều sâu xử lý đồ thị sau:
Sau đó, việc tìm kiếm kết thúc vì tất cả các nodes đã được duyệt.
Độ phức tạp thời gian của tìm kiếm theo chiều sâu là O(n + m) khi n là số lượng đỉnh và m là
số lượng cạnh, vì thuật toán xử lý mỗi đỉnh và mỗi cạnh.
c) Thực thi mã
Tìm kiếm theo chiều sâu có thể được thực thi một cách tiện lợi bằng đệ quy. Hàm dfs sau bắt
đầu một tìm kiếm theo chiều sâu tại một đỉnh xác định. Hàm giả thiết rằng đồ thị được lưu trữ
theo danh sách kề trong một mảng:
vector<int> adj[N];
bool visited[N];
để lưu vết các đỉnh đã viếng thăm. Khởi tạo, mỗi giá trị của mảng là false, và khi
việc tìm kiếm bắt đầu từ đỉnh s, giá trị của visited[s] thành true. Hàm có thể được
thực thi như sau:
void dfs(s) {
if (visited[s]) return;
visited[s] = true;
// process node s
for(auto u : adj[s]) {
dfs(u);
}
}
1. Tìm kiếm theo chiều rộng (Breadth-first search)
Tìm kiếm theo chiều rộng đi qua các đỉnh cấp 1 sau đó mới đến các
đỉnh khác. Đầu tiên, thuật toán tìm kiếm khai phá các đỉnh mà có
khoảng cách đến đỉnh bắt đầu là 1, sau đó các đỉnh có khoảng cách là
2, v.v… Quá trình này được tiếp tục cho đến khi tất cả các đỉnh đã
được duyệt.
b) Ví dụ
Chúng ta cùng xem xét cách tìm kiếm theo chiều rộng xử lý đồ thị sau:
Giả thiết rằng việc tìm kiếm bắt đầu tại đỉnh 1.
Đầu tiên, chúng ta xử lý tất cả các đỉnh có thể đi
đến từ đỉnh 1 bằng một cạnh:
Bây giờ chúng ta tính toán khoảng cách từ đỉnh bắt đầu đến tất cả các đỉnh của đồ thị. Khoảng cách như sau:
Đỉnh Khoảng cách
1 0
2 1
3 2
4 1
5 2
6 3
Giống như tìm kiếm theo chiều sâu, độ phức tạp của tìm kiến theo chiều rộng là O(n + m), khi n là số lượng đỉnh
và m là số lượng cạnh.
c) Thực thi mã
Tìm kiếm theo chiều rộng là khó thực thi mã hơn tìm kiếm theo chiều sâu, vì
thuật toán viếng thăm các đỉnh trong nhiều đường khác nhau của đồ thị. Một
thực thi điển hình là dựa trên một hàng đợi (queue) chứa các đỉnh. Tại mỗi
bước, đỉnh tiếp theo trong hàng đợi sẽ được xử lý.
Mã sau giả thuyết rằng đồ thị được lưu trữ dạng danh sách kề và duy trì một
cấu trúc dữ liệu như sau:
queue<int> q;
bool visited[N];
int distance[N];
Hàng đợi q chứa các đỉnh để xử lý theo thứ tự tăng dần khoảng cách của chúng. Các
đỉnh mới luôn được thêm vào cuối hàng đợi, và một đỉnh tại đầu hàng đợi là đỉnh tiếp
theo được xử lý. Mảng visited chỉ ra các đỉnh mà việc tìm kiếm đã viếng thăm, và mảng
distance sẽ chứa khoảng cách từ đỉnh bắt đầu đến tất cả các đỉnh khác của đồ thị.
Việc tìm kiếm có thể được thực thi như sau, bắt đầu từ đỉnh x:
visited[x] = true;
distance[x] = 0;
q.push(x);
while(!q.empty()) {
int s = q.front(); q.pop()
// process node s
for(auto u : adj[s]) {
if(visited[u]) continue;
visited[u] = true;
distance[u] = distance[s] + 1;
q.push(u);
}
}
THANK'S FOR
WATCHING