图——邻接矩阵的DFS,BFS

	邻接矩阵由于是二维数组,DFS,BFS时间复杂度O(n2)。
	深度优先适合目标比较明确,以找到目标为主要目的的情况。
	广度优先搜索更适合在不断扩大遍历范围时找到相对最优解情况。

一、BFS的介绍
BFS(广度优先搜索,也可称宽度优先搜索)是连通图的一种遍历策略。
使用队列保存未被检测的结点。结点按照宽度优先的次序被访问和进出队列。广度优先搜索(BFS)类似于二叉树的层序遍历算法,是一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索(DFS)那样有回退的情况,因此它不是一个递归的算法,为了实现逐层的访问,算法必须借助一个辅助队列并且以非递归的形式来实现。

二、BFS搜索的步骤
1、首先创建一个visit[ ]数组和一个队列q,分别用来判断该位置是否已经访问过及让未访问过的点入队;
2、初始化visit[ ]数组,清空q队列;
3、让起点start入队,并使该点的visit置1;
4、while(!q.empty()){…}执行搜索操作,
a、取出队头元素后使队头元素出队,判断该元素是否为目标到达点;
b、如果是目标点,就返回结果(一般是最短时间、最短路径);
c、如果不是目标点,就继续访问与其相邻的位置点,将可走的相邻的位置点入队,并更新visit[ ]数组;

代码

#include<iostream>
using namespace std;
#include<queue>
typedef int vertextype;		//顶点类型
typedef int edgetype;		//边上的权值类型
#define max 100			//最大顶点数
#define INFITITY 65535		//65535代表正无穷

class mgraph
{
public:
	vertextype vexs[max];	//顶点表
	edgetype arc[max][max];//邻接矩阵,可看做边表
	int numvertexes, numedges;//顶点个数,边的个数

	mgraph();//初始化

	void createmgraph();//创建
	void DFS(int i);	//邻接矩阵的深度优先递归算法
	void DFStraverse();	//深度优先遍历

	void BFStraverse();	//广度优先遍历
};
mgraph::mgraph()
{
	cout << "请输入顶点数和边数" << endl;
	cin >> this->numvertexes >> this->numedges;
	for (int i = 0; i < numvertexes; i++)
		for (int j = 0; j < numvertexes; j++)
			this->arc[i][j] = INFINITY;		//邻接矩阵初始化
}
void mgraph::createmgraph()
{
	int i , j, w = 0;
	cout << "输入顶点信息,建立顶点表" << endl;
	for (int k = 0; k < numvertexes; k++)		//建立顶点表
		cin >> this->vexs[k];
	for (int m = 0; m< numedges; m++)		//建立numedges条边
	{
		cout << "输入边(Vi,Vj)的上标i,下标j,和权重w " << endl;
		cin >> i >> j >>w ;
		this->arc[i][j] = w;
		this->arc[j][i] = this->arc[i][j];	//无向图,矩阵对称
	}
}
bool visited[max];		// 访问标志的数组

void mgraph::DFS(int i)			//邻接矩阵的深度优先递归算法
{
	
	visited[i] = 1;		//被访问后标记为1
	cout << this->vexs[i] << " ";
	for (int j = 0; j < numvertexes; j++)
		if (this->arc[i][j] == 1 && !visited[j])	//边存在且顶点未访问
			DFS(j);								//对访问的邻接节点递归调用
}

void mgraph::DFStraverse()
{
	int i = 0;
	for (i = 0; i < numvertexes; i++)
		visited[i] = 0;			//初始所以顶点状态为0,即未访问
	for (i = 0; i < numvertexes; i++)
		if(!visited[i])
		DFS(i);				//对未访问的顶点调用DFS。若是连通图,则调用一次
}

bool visited1[max];

void mgraph::BFStraverse()		//广度遍历算法
{

	queue<vertextype> q;//队列
	for (int i = 0; i < numvertexes; i++)	//初始化标记数组
		visited1[i] = 0;
	for (int i = 0; i < numvertexes; i++)	//对每一个顶点做循环
	{
		if (!visited1[i])		//如果顶点未访问就处理
		{
			visited1[i] = 1;
			cout << vexs[i] << " ";
			q.push(i);		//将顶点入队

			while (!q.empty())	//队不为空
			{
				i = q.front();	//把将要出队的元素赋值给i;
				q.pop();		
				
				for(int j=0;j<numvertexes;j++)
				{
					if (arc[i][j]==0&&!visited1[j])	//判断其他顶点与当前顶点存在边,且未被访问
					{
						visited1[j] = 1;
						cout << this->vexs[j] << " ";
						q.push(j);		//将找到的此顶点入队
					}	
				}
			}
		}
	}
}
int main()
{
	mgraph g;
	g.createmgraph();
	g.DFStraverse();
	cout << endl;
	g.BFStraverse();
	system("pause");
	return 0;

}

一、什么是BFS
广度优先算法(Breadth-First-Search),简称BFS,是一种图形搜索演算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,则演算终止。
二、算法分析
1、BFS是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
2、时间复杂度:邻接表的时候是O(V2),邻接表的时候是O(|V| + |E|)。其中 |V| 是节点的数目,|E| 是图中边的数目。
3、适用条件:对于所有边长度相同的情况,比如地图的模型,bfs第一次遇到目标点,此时就一定是从根节点到目标节点最短的路径(因为每一次所有点都是向外扩张一步,你先遇到,那你就一定最短)。bfs先找到的一定是最短的。但是如果是加权边的话这样就会出问题了,bfs传回的是经过边数最少的解,但是因为加权了,这个解到根节点的距离不一定最短。比如1000+1000是只有两段,1+1+1+1有4段,但是bfs返回的经过边数最少的解,这里总长度2000,显然不是最短的路径。此时我们就应该采用Dijkstra最短路算法解决加权路径的最短路了。
4、应用:
1、寻找非加权图(或者所有边权重相同)中任两点的最短路径。
2、寻找其中一个连通分支中的所有节点。(扩散性)
3、bfs染色法判断是否为二分图。
三、细节分析
1、当中为什么要用queue来存点而不是stack呢?
我们来举个例子,目前我们的队列中有一个起始节点。第一轮扩展之后,队列里有起始点的上、右、下、左四个点,现在我们就要把这四个点进行一下一轮轮扩展。也就是下一个队列里面的节点应该是“上”的四个方向、“右”的四个方向、“下”的四个方向、“左”的四个方向的点(已经book过的当然的就不加了)。如果我们用的是stack,对‘上’点,我们把它爆掉,然后由stack的性质把它的四个方向的点丢到队列的头。照理说我们取完“上”点扩展之后之后,应该取的是剩余的右、下、左点来继续完成这一轮队列的扩展,可是stack把“上”节点的四个扩展点扔到了右、下、左三个节点的前面了,(根据我们bfs每一次取的都是队首那个点)这样就下一个取的就是“上”节点的四个扩展点了,这就不对了。反之,queue每次把扩展点扔到后面,对于下一次取队首点继续进行扩展就不会有任何影响。
2、bfs能不能记录路径呢?
当然可以,我们可以把走的每一个点的父节点记录下来,最后挨着回溯回去就可以了。
记忆:
1、只要一看到要找最短的路径:bfs or 最短路。 无权用bfs,有权用最短路。
2、如果第二次回到刚才已走过的点的时候有状态参数变化,那么就不能book。

————————————————
版权声明:本文为CSDN博主「十瓣月亮」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37050329/article/details/80289572


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值