自用ACM模板之BFS(循环)(待完善)

前言:本人是个实力很弱但立志变强的ACM小白(使用C++)。此篇BFS模板目前只有循环实现,等之后学习了队列实现或递归实现会回头补充。

BFS即广度优先搜索,在搜索一个状态下一步变化之后可能产生的状态时,先遍历出所有可能产生的新状态,然后再逐一从新状态出发搜索再下一步变化可能产生的状态。

可用树状图表达:

第一类:保存整棵搜索树

显然第一类和第二类对空间大小的要求是不一样的,能满足的题意要求也不太一样,根据实际情况选用

用一个有限列无限行的二维数组(命名为bfs)储存所有搜索结果,其中一列储存搜索层数(变化步数),其他列储存搜索结果。

辅助变量:

  • ibfs - 总是表示bfs数组下一个空行的下标,其值每搜得一个结果+1;
  • bfslayerlength - 总是表示当前层已搜索出的状态,其值每搜得一个结果+1,该层搜索完毕后其值即为当前层的结果数(用变量bfslayerlengthtemp维护这个值),清零之后再搜索下一层;
  • isanswerfound - 用于辅助搜索到最终所需状态时跳出bfs搜索循环、判定不可能情况等;
  • iibfs - 存储上一层搜索完之后ibfs的值,表示当前层的起点下标。
  • bfslayerlengthtemp - 上面已经提过,用来存储搜索完一层后bfslayerlength的值,因此此值表示上一层的搜索结果数;

以上最后两个变量相结合就可以表示出上一层搜索结果的下标范围,声明它们的目的也在于此(上一层的搜索结果即搜索当前层的出发点)。以下把它们称作维护变量:

搜索过程示例:

刚刚搜索完第3层时:

  bfs[][0] bfs[][1]...    
bfs[0][] 1 状态1    
bfs[1][] 2 状态2 <- iibfs - bfslayerlengthtemp  
bfs[2][] 2 状态3    
bfs[3][] 2 状态4    
bfs[4][] 3 状态5 <- iibfs <- ibfs - bfslayerlength
bfs[5][] 3 状态6    
bfs[6][] 3 状态7    
bfs[7][] 3 状态8    
bfs[8][] 0 0   <- ibfs
...        

给维护变量iibfs、bfslayerlengthtemp赋值,清零bfslayerlength后,准备搜索第4层:

  bfs[][0] bfs[][1]...    
bfs[0][] 1 状态1    
bfs[1][] 2 状态2    
bfs[2][] 2 状态3    
bfs[3][] 2 状态4    
bfs[4][] 3 状态5 <- iibfs - bfslayerlengthtemp  
bfs[5][] 3 状态6    
bfs[6][] 3 状态7    
bfs[7][] 3 状态8    
bfs[8][] 0 0 <- iibfs

<- ibfs - bfslayerlength

<- ibfs

...        

搜索第4层中:

  bfs[][0] bfs[][1]...    
bfs[0][] 1 状态1    
bfs[1][] 2 状态2    
bfs[2][] 2 状态3    
bfs[3][] 2 状态4    
bfs[4][] 3 状态5 <- iibfs - bfslayerlengthtemp  
bfs[5][] 3 状态6    
bfs[6][] 3 状态7    
bfs[7][] 3 状态8    
bfs[8][] 4 状态9 <- iibfs <- ibfs - bfslayerlength
bfs[9][] 4 状态10    
bfs[10][] 4 状态11    
bfs[11][] 0 0   <- ibfs
...        

代码模板

//定义BFS所需变量
short bfs[10005][2];//不一定够用
int ibfs;
int iibfs;
int bfslayerlength;
int bfslayerlengthtemp;
bool isanswerfound;

//BFS
//初始化
for (int i = 0; i < 10005; i++)
{
	bfs[i][0] = 0;
	bfs[i][1] = 0;
}
bfs[0][0] = 1; bfs[0][1] = 1;//设置要搜索的起始状态
ibfs = 1;
iibfs = 1;
bfslayerlengthtemp = 1;
isanswerfound = false;

//开始搜索,此while循环每执行一次,搜索一层
while (true)
{
	bfslayerlength = 0;
	for (int i = iibfs - bfslayerlengthtemp; i < iibfs; i++)
	{

		//在这里写上搜索规则,被搜数即bfs[i][1]...,新搜索结果存储位置为,bfs[ibfs][1]...,每当发生有一个新搜索结果加入bfs数组时一定还要伴随以下这段代码

		bfs[ibfs][0] = bfs[iibfs - 1][0] + 1;//这里的bfs[iibfs - 1][0]即上一层层数
		if ()//括号中写上出现了所需状态时满足的条件,例如bfs[i][1]...==所需状态
		{

			//这里可再写上搜到所需状态之后,要在跳出循环之前执行什么,例如输出步数或层数

			isanswerfound = true;
			break;
		}
		ibfs++;
		bfslayerlength++;


	}
	if (isanswerfound == true)//这里需再加上其他要停止bfs搜索的条件,例如层数超出规定范围、没有新增任何结果(bfslayerlength == 0)等
		break;

	bfslayerlengthtemp = bfslayerlength;
	iibfs = ibfs;
}
if (isanswerfound == false)
{
	//这里写上不可能搜索到所需状态时要做什么
}

 

第二类:“边搜边扔”,只保存当前层和上一层搜索结果,但在搜索树分枝数量庞大时能节省不少空间

用两个无限一维数组bfs、bfstemp分别储存当前层和上一层搜索结果(或无限行有限列二维数组,储存一种状态需要多少个数据就用多少列),第一行(即下标为0的位置)储存该组搜索结果所在层数,其他行储存搜索结果。

先将起始状态放入bfstemp,搜索bfstemp中的状态,搜索结果放入bfs,然后再将bfs拷贝至bfstemp并清空bfs,再次从bfstemp里的数据出发搜索下一层...以此反复循环。

  bfstemp bfs   bfstemp bfs   bfstemp bfs
[0] 2 3   3 4   3 4
[1] 状态2 状态4   状态4 0   状态4 状态8
[2] 状态3 状态5 -> 状态5 0 -> 状态5 状态9
[3] 0 状态6   状态6 0   状态6 状态10
[4] 0 状态7   状态7 0   状态7 状态11
[5] 0 0   0 0   0 ...

辅助变量:

  • ibfs - 总是表示数组bfs的下一空位下标,作为bfs数组终点依据;
  • ibfstemp - 总是表示数组bfstemp的下一空位下标,作为bfstemp数组终点依据;
  • isanswerfound - 作用同第一类;

与第一类相比,第二类只要这么多辅助变量就够了。

代码模板

short bfstemp[10005];
int ibfs;
int ibfstemp;
bool isanswerfound;

//BFS
//初始化
for (int i = 0; i < 10005; i++)
{
	bfs[i] = 0;
	bfstemp[i] = 0;
}
bfstemp[0] = 1; bfstemp[1] = 1;//设置要搜索的起始状态
ibfs = 1;
ibfstemp = 1;
isanswerfound = false;

//开始搜索,此while循环每执行一次,搜索一层
while (true)
{
	ibfs = 1;
	for (int i = 1; i < ibfstemp; i++)
	{

		//这里写上搜索规则,被搜数为bfstemp[i],新结果存储位置为bfs[ibfs],每发生有新搜索结果写入要伴随以下这段代码

		if ()//括号中写上出现了所需状态时满足的条件,例如bfs[i]==所需状态
		{

			//这里可再写上搜到所需状态之后,要在跳出循环之前执行什么,例如输出步数或层数

			isanswerfound = true;
			break;
		}
		ibfs++;


	}
	if (isanswerfound == true)//这里需再加上其他要停止bfs搜索的条件,例如层数超出规定范围、没有新增任何结果(ibfs == 1)等
		break;

	for (int i = 0; i < ibfs; i++)//将bfs拷贝到bfstemp,清零bfs
	{
		bfstemp[i] = bfs[i];
		bfs[i] = 0;
	}
	bfs[0] = bfstemp[0] + 1;//赋值层数
}
if (isanswerfound == false)
{
	//这里写上不可能搜索到所需状态时要做什么
}

修改日志

将代码模板从“先搜索完整层再逐一检查这一层有无出现所需状态”改为“每搜索出一个新状态立即检查其是否为所需状态”(2019.2.5 15:35提交)

转载于:https://my.oschina.net/u/4035395/blog/3008319

你可能感兴趣的:(数据结构与算法)