图的深度优先遍历和广度优先遍历
在数据结构中学习了一下图的深度优先遍历和广度优先遍历,发这个博客来复习一下,并总结一下这种方法。
以这个图(把蓝色圆框当作景区,橙色框里的数字为探索的顺序)(遍历时,先遍历节点编号小的)为例,先讲一下深度优先遍历,如果从1号景区开始遍历,游览完1号,开始挑选一条支路。挑2号景区,游览完后,挑通往3号的支路,深入3号景区,再深入6号景区.此时,发现走不动了,走到头了,于是退回3号景区,发现没别的支路(指除了走过的),再退回2号,此时发现有新的支路,探索4号景区。这时,又发现走到头了,开始退回去,退回2号,没有其他支路(除了走过的),又退回到1号。又挑选了5号景区开始探索,又走到11号景区…按照这个思路,最后我们会把最后探索的就是10号景区,也就探索完了。
总结来说就是一头扎到底的走法。我们选择一条支路,尽可能不断地深入,如果遇到死路就往回退,回退过程中如果遇到没探索过的支路,就进入该支路继续深入。
像这样先深入探索,走到头再回退寻找其他出路的遍历方式(一头扎到地地遍历法),就叫做深度优先遍历(DFS)。
以这个图(在上面的图的基础上,做了修改),讲一下广度优先遍历。依然是遍历时,先遍历节点编号小的。先探索1号景区,再以1号为中心,把所有与1号相邻的景区(就是没有别的景区的为中间的链接点)从小到大的探索,顺序就是先2,再3,再5,再7,最后10。再探索与1号隔一层(就是中间有一个景区隔着)的景区,即4,8,9,10,11号景区。顺序也就是前面说的。为什么是这个顺序呢?和上面从1号开始探索的其实一样。以2为中心把所有与2号相邻的景区,从小到大的探索(已经探索过的就不用看了),只有4号。再以3号为中心,把所有与3号相邻的景区,从小到大的探索,只有6号。再以5号为中心…有9和11。再以7为中心…探索完最后一个8号。
总结来说就是把起点相邻的几个景点玩遍,然后去玩距离起点稍远一些(隔一层)的景点,然后再去玩距离起点更远一些(隔两层)的景点…
像这样一层一层由内而外的遍历方式,就叫做广度优先遍历(BFS)。
下面我以两个简单的例题,来更加详细的阐述这两种遍历,并附上代码
数据结构实验之图论二:图的深度遍历
Description
请定一个无向图,顶点编号从0到n-1,用深度优先搜索(DFS),遍历并输出。遍历时,先遍历节点编号小的。
Input
输入第一行为整数n(0 < n < 100),表示数据的组数。 对于每组数据,第一行是两个整数k,m(0 < k < 100,0 < m < k*k),表示有m条边,k个顶点。 下面的m行,每行是空格隔开的两个整数u,v,表示一条连接u,v顶点的无向边。
Output
输出有n行,对应n组输出,每行为用空格隔开的k个整数,对应一组数据,表示DFS的遍历结果。
Sample
Input
1
4 4
0 1
0 2
0 3
2 3
Output
0 1 2 3
#include<bits/stdc++.h>
using namespace std;
bool a[101][101];
bool vis[101];
int arr[101],tail;
void dfs(int position,int n) //深度优先遍历
{
vis[position]=true; //把走过的位置标记一下,防止后面走回这里时,再次把这个数记录进去
arr[++tail]=position; //把遍历的数一个一个记下来等着下面输出
for(int i=0;i<n;i++) //这里就是以现在的结点为基础,如果它还有其他边,也是按照从小的开始,并且还要是没走过的
{
if(a[position][i]==true&&vis[i]==false) dfs(i,n);
}
}
int main()
{
int n;
cin>>n;
while(n--)
{
int k,m,i;
cin>>k>>m;
for(i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
a[x][y]=a[y][x]=true;
}
for(i=0;i<k;i++) //因为题目要求,先遍历结点小的
{ //主函数的这个循环其实没用,就是为了个人理解,多此一举,直接写dfs(0,k)就行
if(vis[i]==false) dfs(i,k);
}
for(i=1;i<=tail;i++)
{
if(i==1) cout<<arr[i];
else cout<<" "<<arr[i];
}
cout<<endl;
tail=0;
for(int i=0;i<k;i++) //这儿的操作是为了清空,因为有多组输入
{
vis[i]=false;
for(int j=0;j<k;j++)
{
a[i][j]=false;
}
}
}
return 0;
}
数据结构实验之图论五:从起始点到目标点的最短步数(BFS)
Description
在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫。在他们所在的地域,有n个隘口,编号为1…n,某些隘口之间是有通道连接的。其中近卫军团在1号隘口,天灾军团在n号隘口。某一天,天灾军团的领袖巫妖王决定派兵攻打近卫军团,天灾军团的部队如此庞大,甚至可以填江过河。但是巫妖王不想付出不必要的代价,他想知道在不修建任何通道的前提下,部队是否可以通过隘口及其相关通道到达近卫军团展开攻击;如果可以的话,最少需要经过多少通道。由于n的值比较大(n<=1000),于是巫妖王找到了擅长编程的你 =_=,请你帮他解决这个问题,否则就把你吃掉变成他的魔法。为了拯救自己,赶紧想办法吧。
Input
输入包含多组,每组格式如下。
第一行包含两个整数n,m(分别代表n个隘口,这些隘口之间有m个通道)。
下面m行每行包含两个整数a,b;表示从a出发有一条通道到达b隘口(注意:通道是单向的)。
Output
如果天灾军团可以不修建任何通道就到达1号隘口,那么输出最少经过多少通道,否则输出NO。
Sample
Input
2 1
1 2
2 1
2 1
Output
NO
1
#include<bits/stdc++.h>
#include<queue>
using namespace std;
struct node
{
int data;//点
int step; //步数
};
bool visit[1234];//用来记录是否遍历过
bool mp[1001][1001]; //因为map似乎是关键词,所以用了mp用来写地图
int n,m;
struct node x,p;
int bfs(node x)
{
queue<node> q;
q.push(x);
visit[x.data]=true;//把起点放入队列中
while(!q.empty()) //直到队列为空
{
p=q.front(); //读出这个点,因为是以这个点为中心
q.pop(); //弹出
if(p.data==1) //如果找到了终点,就输出到到这个点的步数
{
return p.step;
}
for(int i=1;i<=n;i++) //因为是从小开始,所以从1循环到n
{ //这个循环也就是我上面写的以某某为中心,探索相邻的某某点
if(!visit[i]&&mp[p.data][i]==true)//只要此点没被遍历过,且与当前中心点是连接关系,就放入队列中
{
visit[i]=true;
x.data=i; //把此点的序号记下
x.step=p.step+1; //以当前点走到下一个点,所以步数+1
q.push(x); //放入队列 ,用队列先储存这些点的数据
}
}
}
return 0;
}
int main()
{
while(~scanf("%d%d",&n,&m)) //多组输入
{
int a,b;
memset(mp,false,sizeof(mp)); //格式化
memset(visit,false,sizeof(visit));
for(int i=0;i<m;i++) //写入地图
{
scanf("%d%d",&a,&b);
mp[a][b]=true;
}
x.step=0; //一开始的步数为0
x.data=n; //起点n
if(bfs(x))
printf("%d\n",p.step);
else printf("NO\n");
}
return 0;
}
// 上述代码和思路是参考了我们学校的学长,还有一位大佬的博客的,在下面我会附上链接。如果有什么错误之处,会尽快修改。
链接:https://blog.csdn.net/a2217018103/article/details/90678830