bfs(萌新之人所写)

队列

队列的构建

对于bfs来说,他的一个重要的线性结构是队列,所以这一篇前面主要讲的是队列的构建和使用

队列就是像我们日常生活中的队伍一样,先进先出。如何使这个数出去呢?这就需要两个整型变量充当指针,这个指针并不是我们c语言中的指针,他是一个变量 int head;int tail;这两个变量的值就是数组的下标。对head和tail进行加减,就是数组中的数进行加减。若head++则head指向下一位,虽然前面的那一位没有真正意义上的删除,但是从head开始到tail结束的数组长度却已经减少了,所以我们仍把这种操作看成对数组的删除。对于tail也是同理。

接下来我来构建一个队列。

#include
int main()
{
  int queue[100];//数组使必然需要的
  int head=1;//我习惯于数组从1开始
  int tail=1;//待会进行输入的时候,我们用tail++,数组长度就会跟着变大
  int n;
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  {
      scanf("%d",&queue[tail]);//这样就把数输入的队列中了
      tail++;
  }
  //head指向头,tail指向尾
  return 0;
}

bfs(萌新之人所写)_第1张图片

队列的使用

具体例子(该图来自《啊哈!算法》,非常感谢该书作者让我受益匪浅)

bfs(萌新之人所写)_第2张图片

我按照这个要求来进行构造和使用

按照先前的步骤进行构造。

关键在于如何使用,这个要求里面有几个字眼值得关注,第一是删除,第二就是将最前面移到最后面。删除就是将head++,使头指针指向后一位。而调到后面去则是tail++;quene[tail]=quene[head];head++;tail++为前面调过来的数留出空间,然后再进行赋值。

#include
int main()
{
	int data[100];
	int head=1;
	int tail=1;
	for(int i=1;i<=9;i++)
	{
		scanf("%d",&data[tail]);
		tail++;
	}
	while(head

仔细模拟一下,是不是就是这么一回事。

但在平常的使用中,我们往往使用结构来表示一个队列,按照这个思路不难把结构体形式的队列写出来

#include
struct queen
{
  int data[100];
  int head;
  int tail;	
};
int main()
{
	struct queen q;
	q.head=1;
	q.tail=1;
	int i=1;
	for(int i=1;i<=9;i++)//这个就是用来进行9次循环的,这个变量i没有啥实际用处
	{
		scanf("%d",&q.data[q.tail]);//不是将i作为下标进行输入而是将q.tail来进行下标变量
		q.tail++;
	}
	while(q.head

很多题解都是c++的题解,而c++的队列使有自己的库函数的,为了为了方便我复习的时候能够看懂题解我把c++的队列形式写一下

 

q.push(a)//将a插入队列
q.pop()队首出列
q.empty()判断队首是否为空(相当于while(head

bfs的基础

关于bfs的四个问题

1.怎么样将一个数存入进队列?

2.既然没有用到递归,那么是怎么终止的,终止条件又是怎样的?

3.广度优先搜素的模板又是怎样的?

4.常见的题型

回答

我用一个我自己总结的模板就可以回答上面前三个问题

1.构造队列结构体

2.队列初始化,将数据插入到队列的结构体中,仅指第一个(第一个问题)

3.while(head

4.在while循环中做越界判断,标记判断

5.像插入第一个数据一样,利用循环将每一个数据都插入到队列中。

6.终止条件if(tx==q&&ty==p)break;跳出循环(这个条件是下面这道题的终止条件)(第二个问题)

代码形式的模板

while(!q.empty)
{
	a=q.front();
	q.pop;
	for(枚举所有情况)
	{
		if(本状态合法)
		{
			执行标记指令
			q.bush
		}
	}
}

此图来自(该图来自《啊哈!算法》,非常感谢该书作者让我受益匪浅)

实际问题(上面第四个问题)

小哼小哈走迷宫

bfs(萌新之人所写)_第3张图片

 

​
#include
#include
int dx[5]={0,0,0,1,-1};//x方向数组
int dy[5]={0,-1,1,0,0};//y方向数组
struct note
{
    int x;
    int y;
    int f;//编号
    int step;//步数
};
int main()
{
    struct note que[2501];//队列已经构造完了
    int a[51][51];//地图
    int book[51][51];//标记地图
    int head,tail;
    int i,j,k;
    int n,m;//地图的大小
    int sx,sy;//开始的位置
    int p,q;//结束的位置
    int flag;
    scanf("%d %d %d %d",&sx,&,sy,&p,&q);
    head=1;//队列初始化
    tail=1;//队列初始化
    //插入初始值
    que[tail].x=sx;
    que[tail].y=sy;
    que[tail].f=0;
    que[tail].step=0;
    tail++;//如果看了前面队列的构建,这一步不难理解
    book[sx][sy]=1;//这个刚开始的地方需要标记为1
    while(headn||ty<1||ty>n)//是否越界
                continue;
            if(a[tx][ty]==0&&book[tx][ty]==0)//是否遇到障碍
            {
                book[tx][ty]=1;//标记这个点
                que[tail].x=tx;//这一步是不是和dfs(tx,ty)很像
                que[tail].y=ty;//这一步是不是和dfs(tx,ty)很像
                que[tail].step=que[head].step+1;//这里一定要记得,tail的改变是在head的基础上
                tail++;//将队列向后延深为下以一个要入队的数留出空间
            }
            if(tx==p&&ty==q)
            {
                flag=1;
                break;
            }
        }
        if(flag==1)//如果找到了那么就一定是最快找到的,所以退出
            break;
        head++;//如果for循环里面flag!=1,就说明没有找到终点,那队列就要往后移动对每一个可以移动的数进行扩张搜素,将前一个数删除。
    }
    printf("%d ",que[tail-1].step);//由于尾指针是指向最后一个还要后买你一个,所以输出的时候就要减一
    return 0;
}

​

马的遍历

bfs(萌新之人所写)_第4张图片

 在上一道的基础上,这一题的思路与上一题相似。在我还没开始学bfs的时候,最令我费神的使这个“日”该怎么走。解决方案,只需要设置一个方向数组,之前我们学dfs的时候,写过上,下,左,右四个方向的,也写过上,下,左,右,右上,右下,左上,左下的。按照我们之前写这些方向数组的思路,就可以将“日”的方向数组搞定。

那这里又会有一个问题,这里没有终点,怎么终止。既然没有终点,那我们只需要静等while(head

ok!既然这些疑点已经明白,那就套用模板,代码如下

#include
#include
#include
int a[400][400];
int book[400][400];
int dx[9]={0,1,2,2,1,-1,-2,-2,-1};//“日”x的方向数组
int dy[9]={0,-2,-1,1,2,2,1,-1,-2};//“日”y的方向数组
struct note
{
    int x;
    int y;
    int step;
};
int main()
{
    memset(a,-1,sizeof(a));//因为没有走到的位置要求为-1,所以我们就令这个a数组全部为-1
    struct note q[160010];
    int n,m;//表示地图的大小
    int sx,sy;//表示开始的位置
    scanf("%d %d %d %d",&n,&m,&sx,&sy);
    int tail=1;
    int head=1;
    q[tail].x=sx;
    q[tail].y=sy;
    q[tail].step=0;
    tail++;//这里将其队列全部初始化
    a[sx][sy]=0;//最开始的位置步数为0
    book[sx][sy]=1;//标记最开始的位置,防止重复走
    while(headn||ty<1||ty>m)
                continue;
            if(book[tx][ty]==0)
            {
                book[tx][ty]=1;
                q[tail].x=tx;
                q[tail].y=ty;
                q[tail].step=q[head].step+1;//这里一定要注意,这个q[tail]和q[head]的关系是前后关系,tail的值取决与前面的head
                a[tx][ty]=q[tail].step;
                tail++;
            }
        }
        head++;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

出现的错误和些许经验总结

在使q[tail].step增加的时候,我直接写成了q[]tail].step++。在我们处理队列的时候,head和tail的关系有点类似与数组中a[i]和a[i+1]的关系,使一种相邻关系,所以后者的值的变化往往取决于前者的基础之上。

还是关于队列的处理。有模板固然好,但最好还是按照自己的思路推一遍

思路:最开始head和tail使重合的,我们将第一个放在head和tail重合的位置上,所以我们要留出空间给下一个要插进来的数,所以才tail++,要记住,除了赋值的时候,tail始终指向空。当一系列操作进行完后,队列才向前移动。

你可能感兴趣的:(宽度优先,算法,数据结构)