三、【图算法】广度优先搜索(BFS)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18108083/article/details/84872667

图算法是个庞大的家族,其中大部分成员的主体框架都可以归结于图的遍历。图的遍历需要访问所有顶点一次且仅 一次,此外,图遍历同时还要访问所有的边一次且仅一次。经过一次遍历,树边的顶点共同构成了原图的一颗遍历树

为防止对顶点的重复访问,在遍历的过程中,需要动态地设置各顶点的不同状态,并且随着遍历的进程不断地转换状态,直至最后的“访问完毕”,图的遍历更加强调对处于特定状态顶顶啊的甄别和查找,故也称作图搜索

最基本而典型的图搜索算法包括:广度优先搜索(BFS),深度优先搜索(DFS),优先级搜索等(PFS),本文主要介绍图的广度优先搜索(breadth-first search,BFS),本文使用的图数据结构参见之前博客https://blog.csdn.net/qq_18108083/article/details/84870399

策略:越早被访问到的顶点,其邻居顶点越优先被选用。始自图中顶点s的BFS搜索,将首先访问顶点s,再依次访问顶点s所有未成访问过的邻居顶点,再按照后者被访问的先后次序,组个访问它们的邻居。这个过程很类似与树的层次遍历,具有先入先出的特点,故采用队列结构作为辅助容器。

实现:由于整个图可能具有多个连通域,从单个顶点开始的BFS可能不能遍历到图中的所有顶点,所以BFS函数能够遍历从顶点s开始的单个连通域,而bfs函数则对所有顶点进行检查,只要未曾被访问过,就从该点开始一次新的BFS搜索,这样就能保证所有的连通域都能够被遍历到。

template<typename Tv, typename Te> void graph<Tv, Te>::BFS(int v, int& clock)   //遍历单个
{
	queue<int> Q;  //顶点缓存队列
	status(v) = DISCOVERED; //标记顶点为已发现
	Q.enqueue(v);  //将当前顶点入队
	
	while (!Q.empty())   //只要队列非空,则继续
	{
		v = Q.dequeue();  //每次选择一个顶点出队,遍历其所有邻居,检查是否存在关联边
		dTime(v) = ++clock;
		for (int u = firstNbr(v); u >= 0; u = nextNbr(v, u))   //对于顶点v,从顶点集V的最后一个元素开始寻找邻居(与v存在关联边)
		{

			if (status(u) == UNDISCOVERED)  //如果此邻居顶点尚未发现
			{			cout << "(v,u)" << "(" << v<<"," << u << ")" << endl;
				status(u) = DISCOVERED;  //设置该顶点为已被发现
				type(v, u) = TREE;   //设置边e(v,u)为TREE(遍历树)
				parent(u) = v;       //设置在遍历树中顶点u的父亲为v
				Q.enqueue(u);        //顶点u入队
			}
			else  //如果此邻居顶点已经被发现
			{
				type(v, u) = CROSS;   //设置边e(v,u)为CROSS(跨边),不是遍历树枝干
			}
		}
		status(v) = VISITED;   //设置顶点v为已遍历
	}
}

template<typename Tv, typename Te> void graph<Tv, Te>::bfs(int s)
{
	reset();   //复位所有顶点和已存在边的状态为未被发现,未确定
	int clock = 0;  //时间标签
	int v = s;
	do
	{
		if(status(v)==UNDISCOVERED)
			BFS(v,clock);   //对每个顶点都进行一次单连通域广度优先搜索
		v++;
		cout << "v----" << v << endl;
	} while ((v = (++v%n)) != s);
}

效率:若图G=(V,E)中共有n个顶点和e条边,则BFS仅需O(n+e)时间。

猜你喜欢

转载自blog.csdn.net/qq_18108083/article/details/84872667