1. Tìm theo chiều sâu (Depth First Search) * Ý tưởng:
- Từ đỉnh v1 nào đó chưa thăm, thăm v1, rồi tìm đỉnh v2 (chưa thăm) kề với v1, thăm v2…
- Nếu tại một đỉnh vi nào đó, không còn đỉnh nào kề với vi là chưa thăm thì quay trở lại tìm đỉnh kề chưa thăm khác của vi-1 và thăm đỉnh này.
- Thuật toán lặp lại việc thăm cho tới khi tất cả các đỉnh đều được thăm. Nếu bắt đầu từ đỉnh V1, thì thứ tự thăm có thể là:
V1,V2,V3,V4,V5. V2 V3 V4 V1 V5 * Thuật toán DFS: * Ví dụ : Hình 1 * Nhận xét:
- Mỗi đỉnh sẽ được thăm đúng một lần. void DFS1(v)//duyệt một thành phần liên thông {
Tham_Dinh(v);
tham[v]=1; //ghi nhận là đã thăm v để về sau không thăm nữa. For (u∈ Ke(v)) // xét tất cả các đỉnh u kề với v
If (!tham[u]) DFS1(u); //neu u chua thăm, thăm u }
void DFS()//duyệt tất cả các thành phần liên thông {
for (v ∈V) tham[v]=0; //ban đầu tất cả các đỉnh đều chưa thăm. for (v∈ V) //xét tất cả các dinh
if (!tham[v]) DFS1(v); // neu đỉnh v chua tham thi tham v }
Giả thiết các đỉnh trong danh sách kề của đỉnh v (Ke(v)) được sắp xếp theo chỉ số tăng dần. Khi đó chỉ số trong ngoặc là thứ tự được thăm.
- DFS1(v) thăm tất cả các đỉnh thuộc cùng một thành phần liên thông chứa đỉnh v. Số lần DFS gọi DFS1 chính là số thành phần liên thông của đồ thị.
- Độ phức tạp của thuật toán là O(n+m).
2. Tìm theo chiều rộng (Breadth First Search) * Ý tưởng:
B0: Từ đỉnh v nào đó chưa thăm, cất v vào hàng đợi.
B1: Lấy từ hàng đợi một đỉnh v, thăm v, rồi cất các đỉnh u chưa thăm kề với v vào hàng đợi… B2: Lặp lại B1 cho tới khi hàng đợi rỗng.
* Thuật toán BFS:
* Ví dụ:
* Nhận xét:
- Mỗi đỉnh sẽ được thăm đúng một lần.
- BFS1(v) thăm tất cả các đỉnh thuộc cùng một thành phần liên thông chứa đỉnh v. Số lần BFS gọi BFS1 chính là số thành phần liên thông của đồ thị.
void BFS1(v)//duyệt một thành phần liên thông {
queue=φ; //khoi tạo hàng đợi rỗng
push(queue,v); //cất v vào hàng đợi
tham[v]=1; //ghi nhận là đã thăm v để về sau không thăm nữa. while (queue ≠ φ) // trong khi hàng đợi còn khác rỗng
{
v=pop(queue);//lay v từ hàng đợi Tham_Dinh(v);
for (u∈ Ke(v)) // xét các đỉnh u kề với v if (!tham[u])//nếu u chưa thăm {
push(queue,u); //cất u vào hàng đợi tham[u]=1;//ghi nhan u tham roi }
}} }
void BFS()//duyệt tất cả các thành phần liên thông {
for (v ∈V) tham[v]=0; //ban đầu tất cả các đỉnh đều chưa thăm. for (v∈ V) //xét tất cả các dinh
if (!tham[v]) BFS1(v); // neu đỉnh v chua tham thi tham v } 1 2 3 4 5 6 7 8 9 10 11 12 13 1
- Độ phức tạp của thuật toán là O(n+m). 3. Tìm đường đi và kiểm tra tính liên thông
a) Bài toán tìm đường đi giữa hai đỉnh:
Giả sử s và t là hai đỉnh nào đó của đồ thị. Hãy tìm đường đi từ s đến t.
* Ý tưởng:
Gọi thủ tục DFS(s) hoặc BFS(s) để thăm tất cả các đỉnh thuộc cùng một thành phần liên thông với s. Nếu sau khi thực hiện xong thủ tục mà thăm[t]=false thì không có đường đi từ s đến t, ngược lại thì có đường đi từ s đến t. Để ghi nhận đường đi, ta dùng thêm biến Trước[v] để ghi nhận đỉnh ở trước đỉnh v trên đường đi từ s đến v.
* Thuật toán:
* Chú ý:
Đường đi tìm theo BFS là đường đi ngắn nhất theo số cạnh từ s đến t nhưng DFS thì không. b) Bài toán tìm các thành phần liên thông:
void DFS1(v)//duyệt một thành phần liên thông {
Tham_Dinh(v);
tham[v]=1; //ghi nhận là đã thăm v để về sau không thăm nữa. For (u∈ Ke(v)) // xét tất cả các đỉnh u kề với v
if (!tham[u]) //nếu u chưa thăm {
truoc[u]=v; //trước u là v, lệnh mới thêm vào
DFS1(u); //thăm u }
}
void BFS1(v)//duyệt một thành phần liên thông {
queue=φ; //khoi tạo hàng đợi rỗng
push(queue,v); //cất v vào hàng đợi
tham[v]=1; //ghi nhận là đã thăm v để về sau không thăm nữa. while (queue ≠ φ)
{ v=pop(queue);//lay v từ hàng đợi Tham_Dinh(v);
for (u∈ Ke(v)) // xét các đỉnh u kề với v if (!tham[u])//nếu u chưa thăm
{ truoc[u]=v;//trước u là v, lệnh mới thêm vào
push(queue,u); //cất u vào hàng đợi tham[u]=1;//ghi nhan u tham roi } } } void main() { DFS1(s); //BFS1(s);
if (!tham[t]) printf(“khong co duong di tu %d den %d”,s,t); else { printf(“duong di tu %d den %d :”,s,t ); printf(t) ; do { v=truoc[t]; printf( ‘ ‘,v) ; }while(v !=s); } }
Hãy cho biết đồ thị có bao nhiêu thành phần liên thông và mỗi thành phần liên thông gồm những đỉnh nào?
* Ý tưởng:
- Do số thành phần liên thông bằng số lần DFS() gọi DFS1() hoặc BFS() gọi BFS1(), nên ta dùng biến stplt để đếm số thành phần liên thông, mỗi lần DFS() gọi DFS1() hoặc BFS() gọi BFS1() ta tăng biến stplt lên 1.
- Khi thăm v thay vì gán thăm[v]=true(=1) ta gán thăm[v]=stplt (số hiệu thành phần liên thông chứa v).
* Thuật toán:
void DFS1(v) //duyệt một thành phần liên thông { Tham_Dinh(v);
tham[v]=stplt; //ghi nhận là đã thăm v để về sau không thăm nữa. For (u∈ Ke(v)) // xét tất cả các đỉnh u kề với v
If (!tham[u]) DFS1(u); //neu u chua thăm, thăm u }
void DFS() //duyệt tất cả các thành phần liên thông {
for (v ∈V) tham[v]=0; //ban đầu tất cả các đỉnh đều chưa thăm. stplt=0;
for (v∈ V) //xét tất cả các dinh if (!tham[v])
{ stplt++;
DFS1(v); // neu đỉnh v chua tham thi tham v }
}
void BFS1(v)//duyệt một thành phần liên thông {
queue=φ; //khoi tạo hàng đợi rỗng
push(queue,v); //cất v vào hàng đợi
tham[v]=stplt; //ghi nhận là đã thăm v để về sau không thăm nữa. while (queue ≠ φ) // trong khi hàng đợi còn khác rỗng
{ v=pop(queue);//lay v từ hàng đợi Tham_Dinh(v);
for (u∈ Ke(v)) // xét các đỉnh u kề với v if (!tham[u])//nếu u chưa thăm
{ push(queue,u); //cất u vào hàng đợi tham[u]=stplt;//ghi nhan u tham roi }
}} }
void main()
{ DFS(); //BFS();
if (stplt==1) printf("\nDo thi lien thong"); else
{ printf("\nSo tplt: %d",stplt); for (i=1;i<=stplt;i++)
{ printf("\nTplt thu %d:",i ); for (j=0;j<n;j++)//in tplt thu i
if (tham[j]==i) printf ("%d ",j+1); }
}} }
CHƯƠNG 4