圖的遍歷有兩種遍歷方式:深度優先遍歷(depth-first search)和廣度優先遍歷(breadth-first search)。
DFS通常使用遞歸實現,BFS通常使用隊列實現。圖的遍歷是樹的遍歷的推廣,是按照某種規則(或次序)訪問圖中各頂點依次且僅一次的操作,亦是將網絡結構按某種規則線性化的過程。
1.DFS
基本思想:
首先從圖中某個頂點v0出發,訪問此頂點,然後依次從v0相鄰的頂點出發深度優先遍歷,直至圖中所有與v0路徑相通的頂點都被訪問了;若此時尚有頂點未被訪問,則從中選一個頂點作爲起始點,重複上述過程,直到所有的頂點都被訪問。可以看出深度優先遍歷是一個遞歸的過程。
例:
輸入一個長和寬分別爲m和n的圖,該圖由*號和#號構成,求#號的連通塊數。
如:
****#
****#
*#**#
*##**
*#***
此圖有兩個連通塊。
代碼:
//DFS求連通塊
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
//#define file
#define maxn 105
using namespace std;
struct data{
char pic;
int idx;
};
data atlas[maxn][maxn];
int m,n;
void dfs(int x,int y,int id)
{
if(x<0||x>m||y<0||y>n)
return;
if(atlas[x][y].idx>0||atlas[x][y].pic!='@')
return;
atlas[x][y].idx=id;
for(int nx=-1;nx<=1;nx++)
{
for(int ny=-1;ny<=1;ny++)
{
if(nx!=0||ny!=0)
dfs(x+nx,y+ny,id);
}
}
}
int main()
{
#ifdef file
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif //
cin>>m>>n;
for(int j=0;j<n;j++)
{
for(int i=0;i<m;i++)
{
cin>>atlas[i][j].pic;//用cin可以過濾不可見字符 如回車
}
}
int countn=0;
for(int j=0;j<n;j++)
{
for(int i=0;i<m;i++)
{
if(atlas[i][j].pic=='@'&&atlas[i][j].idx==0)
dfs(i,j,++countn);
}
}
cout<<countn<<endl;
return 0;
}
2.BFS
基本思想:首先,從圖的某個頂點v0出發,訪問了v0之後,依次訪問與v0相鄰的未被訪問的頂點,然後分別從這些頂點出發,廣度優先遍歷,直至所有的頂點都被訪問完。
實現方法,先將頂點加入隊列,然後每出列一個元素,就將該元素的子節點加入隊列,依次遍歷直到搜索到第一個解。
BFS的典型例子應該就是走迷宮了,下面是一個求走迷宮最短路的程序,輸入一個迷宮,1代表牆,0代表路,2代表入口,3代表出口,求入口到出口的最短距離。
代碼:
//BFS走迷宮
#include<queue>
#include<iostream>
#include<string>
#include<cstdio>
//#define file
#define maxn 101
using namespace std;
struct data{
int step;
int x;
int y;
};//
int mis[maxn][maxn];//儲存地圖
data start,eend,temp,temp2;//開始點,終點,臨時變量
int main()
{
#ifdef file
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif // file0;
queue<data> s;//用隊列儲存數據
int inx,iny,outx,outy;
int m,n;
cin>>m>>n;
bool flag=false;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=m;i++)
{
cin>>mis[i][j];
if(mis[i][j]==2)
{//記錄開始點
start.x=i;
start.y=j;
start.step=0;
}
if(mis[i][j]==3)
{//記錄終點
eend.x=i;
eend.y=j;
eend.step=0;
}
}
}
s.push(start);
while(!flag)
{
temp=s.front();
s.pop();
if(temp.y-1>=1)
{//判斷是否出界
if(mis[temp.x][temp.y-1]==0||mis[temp.x][temp.y-1]==3)
{//當所判斷點不是牆,入隊
mis[temp.x][temp.y-1]=1;
temp2.step=temp.step+1;
temp2.y=temp.y-1;
temp2.x=temp.x;
if(temp2.x==eend.x&&temp2.y==eend.y)
{//到終點,標記,結束搜索
s.push(temp2);
flag=true;
}
s.push(temp2);
}
}//up
if(temp.x+1<=m)
{
if(mis[temp.x+1][temp.y]==0||mis[temp.x+1][temp.y]==3)
{
mis[temp.x+1][temp.y]=1;
temp2.step=temp.step+1;
temp2.y=temp.y;
temp2.x=temp.x+1;
if(temp2.x==eend.x&&temp2.y==eend.y)
{
s.push(temp2);
flag=true;
}
s.push(temp2);
}
}//right
if(temp.y+1<=n)
{
if(mis[temp.x][temp.y+1]==0||mis[temp.x][temp.y+1]==3)
{
mis[temp.x][temp.y+1]=1;
temp2.step=temp.step+1;
temp2.y=temp.y+1;
temp2.x=temp.x;
if(temp2.x==eend.x&&temp2.y==eend.y)
{
s.push(temp2);
flag=true;
}
s.push(temp2);
}
}//down
if(temp.x-1>=1)
{
if(mis[temp.x-1][temp.y]==0||mis[temp.x-1][temp.y]==3)
{
mis[temp.x-1][temp.y]=1;
temp2.step=temp.step+1;
temp2.y=temp.y;
temp2.x=temp.x-1;
if(temp2.x==eend.x&&temp2.y==eend.y)
{
s.push(temp2);
flag=true;
}
s.push(temp2);
}
}//left
}
while(!s.empty())
{
temp=s.front();
s.pop();
}
cout<<temp.step<<endl;
return 0;
}