图的深度优先遍历和广度优先遍历

图的深度优先遍历和广度优先遍历

在数据结构中学习了一下图的深度优先遍历和广度优先遍历,发这个博客来复习一下,并总结一下这种方法。
在这里插入图片描述
  以这个图(把蓝色圆框当作景区,橙色框里的数字为探索的顺序)(遍历时,先遍历节点编号小的)为例,先讲一下深度优先遍历,如果从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
  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值