图的遍历有两种遍历方式:深度优先遍历(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;
}