Tư tưởng chính của phương pháp tìm kiếm theo chiều rộng trên đồ thị có thể được hiểu như sau: Ban đầu tất cả các đỉnh của đồ thị là chưa được xét đến, ta sẽ bắt đầu việc tìm kiếm từ một đỉnh nào đó của đồ thị, giả sử đỉnh đó là v1. Khi duyệt đỉnh v1 ta sẽ để ý tới tất cả các đỉnh v11, v12, .., v1k
kề với đỉnh v1 mà chưa được xét đến để ngay sau đó lần lượt xét tới các đỉnh này, khi duyệt đỉnh v1i
(i=1,2,..k) ta lại để ý tới tất cả các đỉnh kề với nó mà chưa được xét đến để rồi lần lượt xét đến các đỉnh đó. Qua trình sẽ cứ như vậy cho đến khi nào tất cả các đỉnh của đồ thị đều được xét hết. Ta có thể hình dung phương pháp này như hình ảnh của vết dầu loang, từ một điểm trên mặt phẳng dầu sẽ loang sang ngay các điểm lân cận với điểm đó.
Với phương pháp này ta thấy rằng các đỉnh kề với một đỉnh của đồ thị sẽ được xếp hàng theo thứ tự để được lần lượt xét tới, do đó chúng ta có thể dùng cơ chế hàng đợi để thực hiện công việc này. Thủ tục mô tả phương pháp này như sau
Procedure BFS(v)
(* tìm kiếm theo chiều rộng bắt đầu từ đỉnh v *) (* Các biến Chuaxet, Ke là toàn cục *)
Begin
Queue : = φ
Queue ⇐ v; (* Nạp v vào Queue *) Chuaxet[v]:=False;
While Queue ≠φ do Begin
p ⇐ Queue; (* Lấy p ra khỏi Queue *) Xet_dinh(p); For u ∈Ke(p) do If Chuaxet[u] then Begin Queue ⇐ u; Chuaxet[u]:=False; End; End; End; (* Chương trình chính thực hiện thủ tục *) BEGIN
(* Khởi tạo biến toàn cục Chuaxet *) For v ∈ V do Chuaxet[v]:=True; (* Duyệt các đỉnh *) For v ∈ V do If Chuaxet[v] then BFS(v); END.
Với thuật toán này ta cũng thấy rằng mỗi lệnh gọi BFS(v) sẽ thực hiện duyệt qua các đỉnh cùng thành phần liên thông với đỉnh v. Thủ tục BFS sẽ được thực hiện lần lượt với các đỉnh chưa được duyệt của đồ thị, do đó nó sẽ duyệt hết tất cả các đỉnh của đồ thị. Mặt khác, mỗi khi duyệt xong đỉnh v, biến Chuaxet[v] cũng được gán giá trị False nên mỗi đỉnh sẽ được thăm đúng một lần.
Lập luận tương tự thuật toán tìm kiếm theo chiều sâu ta cũng có được độ phức tạp tính toán của thuật toán này là O(n+m).
Ví dụ 3
Xét đồ thị vô hướng cho ở ví dụ 1 (hình 3.1)
Khi đó thứ tự các đỉnh được duyệt theo thuật toán tìm kiếm theo chiều rộng sẽ là: 1 2 3 4 5 8 9 10 6 7
Ví dụ 4
Xét đồ thị có hướng cho ở ví dụ 2 (Hình 2)
Khi đó thứ tự các đỉnh được duyệt theo thuật toán tìm kiếm theo chiều rộng sẽ là: 1 3 2 4 5 7 8 9 6
Dưới đây là chương trình cài đặt hai thuật toán tìm kiếm theo chiều sâu và tìm kiếm theo chiều rộng bằng ngôn ngữ lập trình C. Chương trình xử lý trên đồ thị được cho bởi danh sách kề.
//--- // huong trinh cai dat cac thuat toan tim kiem tren do thi // Depth First Search - Breadth First Search //--- #include<conio.h>
#include<stdio.h> #include<stdlib.h>
#define VMAX 100 //So dinh toi da cho mot do thi typedef struct pp //Cau truc tu tro
{ int v;
struct pp *next; }Link;
Link *Ke[VMAX]; //Danh sach ke cua do thi
int chuaxet[VMAX]; //Bien mang dung de danh dau cac dinh da xet Link *Queue; //Hang doi luu thu tu cac dinh se xet
int n; //So dinh cua do thi
//--- // Ham nhap danh sach ke cua do thi co n dinh //--- void nhap_dsk(Link *Ke[], int n)
{ int i,v; Link *pd,*p;
//Khoi tao mang cac danh sach cac dinh ke cua cac dinh for(i=1;i<=n;i++) { Ke[i] = (Link*)malloc(sizeof(Link)); Ke[i]->v=i; Ke[i]->next = NULL; }
//Nhap danh sach cac dinh ke cua cac dinh for(i=1;i<=n;i++)
{
pd = NULL;
printf("\nNhap cac dinh ke voi dinh %d (nhap 0 de ket thuc!):",i); while(1)
scanf("%d",&v); if(v==0) break; if(pd == NULL) { pd = (Link*)malloc(sizeof(Link)); p=pd; } else { p->next = (Link*)malloc(sizeof(Link)); p=p->next; } p->v=v; p->next = NULL; } Ke[i]->next = pd; } } //--- // Ham hien thi danh sach ke cua do thi co n dinh //--- void in_dsk(Link *Ke[], int n)
{ int i; Link *pd;
printf("\nDanh sach ke cua cac dinh cua do thi:\n"); printf("\n ---\n");
for(i=1;i<=n;i++) {
printf("\n Danh sach cac dinh ke cua dinh %d:",Ke[i]->v); pd = Ke[i]->next; while(pd!=NULL) { printf("%5d",pd->v); pd=pd->next; } } } //--- // Ham nap mot phan tu (mot dinh ) vao hang doi //--- void Push(Link *u)
{ Link *q,*p; q=(Link*)malloc(sizeof(Link)); q->v=u->v; q->next=NULL; if(Queue==NULL)
Queue = q; else { p=Queue; while(p->next!=NULL) p=p->next; p->next = q; } } //--- // Ham lay mot phan tu trong hang doi ra //--- int Pop()
{
if(Queue==NULL) return 0; //Quee rong! Link *p;
p=Queue; Queue=p->next; int t=p->v;
free(p); //Giai phong p return t;
}
//---
// Ham de quy tim kiem theo chieu sau bat dau tu mot dinh //---
void dfs(Link *u) { printf("%3d",u->v); chuaxet[u->v]=0; Link *p = Ke[u->v]->next; while(p!=NULL) { if(chuaxet[p->v]) dfs(p); p=p->next; } } //--- // Ham tim kiem theo chieu rong bat dau tu mot dinh //--- void bfs(Link *u)
{
Queue=NULL; //Khoi tao hang doi Push(u);
chuaxet[u->v]=0; while(Queue!=NULL) {
u=Pop(); printf("%5d",u); Link *p=Ke[u]->next; while(p!=NULL) { if(chuaxet[p->v]) { Push(p); chuaxet[p->v]=0; } p=p->next; } } } //--- // Ham in tieu de cua chuong trinh //--- void tieu_de()
{
printf("\n CHUONG TRINH CAI DAT CAC THUAT TOAN TIM KIEM TREN DO THI"); printf("\n ---***---\n\n");
}
//--- // Ham hien thi Menu chon chuc nang cua chuong trinh //--- char menu()
{
printf("\n Menu chon chu nang"); printf("\n ---***---\n");
printf("\n\n 1. Nhap do thi cho boi danh sach ke"); printf("\n\n 2. Hien thi danh sach ke cua do thi"); printf("\n\n 3. Tim kiem theo chieu sau tren do thi"); printf("\n\n 4. Tim kiem theo chieu rong tren do thi"); printf("\n\n 5. Ket thuc chuong trinh");
printf("\n\n ---"); printf("\n\n Ban chon:"); char ch=getche(); return ch;
}
//--- // Chuong trinh chinh
//--- void main() { int kt=0,i; char ch; do { clrscr(); tieu_de();
ch = menu(); switch(ch) {
case '1': //Nhap danh sach ke cua do thi clrscr();
tieu_de(); kt=1;
printf("\n\n1.Nhap danh sach ke cua do thi"); printf("\n---\n\n");
printf("\n\nSo dinh cua do thi n ="); scanf("%d",&n); nhap_dsk(Ke,n);
printf("\n\n\n\n---");
printf("\nGo mot phim bat ky de tro ve menu chon chuc nang!"); getch();
break;
case '2': //Hien thi danh sach ke cua do thi clrscr();
tieu_de();
printf("\n\n2.In danh sach ke cua do thi"); printf("\n---\n\n"); if(kt)
in_dsk(Ke,n); else
printf("\nDo thi chua duoc nhap vao!"); printf("\n\n\n\n---");
printf("\nGo mot phim bat ky de tro ve menu chon chuc nang!"); getch();
break;
case '3': //Tim kiem theo chieu sau tren do thi clrscr();
tieu_de();
printf("\n\n3.Tim kiem theo chieu sau tren do thi"); printf("\n---\n\n"); if(kt)
{
//Khoi toa bien chuaxet; for(i=1;i<=n;i++) chuaxet[i]=1;
//Ket qua tim kiem theo chieu sau
printf("\n\nThu tu cac dinh duoc xet theo chieu xau:\n\n"); for(i=1;i<=n;i++)
if(chuaxet[i]) dfs(Ke[i]); }
else
printf("\nDo thi chua duoc nhap vao!"); printf("\n\n\n\n---");
printf("\nGo mot phim bat ky de tro ve menu chon chuc nang!"); getch();
case '4': //Tim kiem theo chieu rong tren do thi clrscr();
tieu_de();
printf("\n\n4.Tim kiem theo chieu rong tren do thi"); printf("\n---");
if(kt) {
//Khoi toa bien chuaxet; for(i=1;i<=n;i++) chuaxet[i]=1;
//Ket qua tim kiem theo chieu rong
printf("\n\nThu tu cac dinh duoc xet theo chieu rong:\n\n"); for(i=1;i<=n;i++)
if(chuaxet[i]) bfs(Ke[i]); }
else
printf("\nDo thi chua duoc nhap vao!"); printf("\n\n\n\n---");
printf("\nGo mot phim bat ky de tro ve menu chon chuc nang!"); getch();
break;
case '5': //Ket thuc chuong trinh
printf("\n\nXin cam on ban da su dung chuong trinh!"); getch();
break; }
}while(ch!='5'); }