队列相关定义
队列是一种线性存储表,元素数据的插入在表的一端进行,在另一端删除,从而构成了一个先进先出的FIFO(First In First Out)表。
队头&队尾:插入一端称为队尾,删除一端称为队头。
STL——queue
对象定义
*queue
模版类的定义在<queue>
头文件中。
*queue
模版类需要两个模版参数,一个是元素类型,一个是容器类型。
定义queue
对象的示例代码如下:
queue<int> q1;
queue<double> q2;
定义模版:queue<Type> S
,Type
可以是int,double,float,char,string
等类型,还是是结构体类型的。
基本操作
入队(push):在队列末尾加入一个元素,例如:q.push(x)
出队(pop):删除/弹出队列第一个元素,即队首元素,例如:q.pop()
访问队首元素(front):返回队列第一个元素,例如:q.front()
访问队尾元素(back):返回队列最后一个元素,即队尾元素,例如:q.back()
判断队列空(empty):判断队列是否为空,如果空,则返回true,否则返回false,例如:q.empty()
访问队列中元素的个数(size):返回队列元素个数,例如:q.size()
代码实现
#include<iostream>
#include<queue>
using namespace std;
int main()
{
queue<int> num;
int c;
cout<<"输入10个元素,并将其加入队列中:"<<endl;
for(int i=0;i<10;i++)
{
cin>>c;
num.push(c);
}
cout<<"计算队列中元素个数:"<<num.size()<<endl;
cout<<"输出队列中10个元素:"<<endl;
while(!num.empty())
{
int t=num.front();
cout<<t<<" ";
num.pop();
}
cout<<"队列是否空:";
if(num.empty()) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
queue队列的实现
实现队列,需要操作push(),pop(),front(),empty()
广度优先搜索(BFS)
基本思想
广度优先搜索又称宽度优先搜索,一般用来解决最短路径问题。
广搜的目标是,一旦找到一条从起点到目标结点的路径,这条路径就一定是最优的(步数最少)的。
广搜是按怎样顺序来搜索的呢?
有这样一棵树:
我们要想广度优先,那么首先搜索第一层1,然后搜索第二层,从左向右2,5,6,9,在搜索第三层,从左向右3,4,7,10,再搜索第四层8,很简单吧,这就是一个层次遍历。
程序的实现需要借助队列。
(1)首先,令树根1入队,如图:
1 |
---|
**(2)**队头元素出队,输出1,同时令1的所有孩子(从左向右顺序)入队,如图:
2 | 5 | 6 | 9 |
---|
**(3)**队头元素出队,输出2,同时令2的所有孩子(从左向右顺序)入队,如图:
5 | 6 | 9 | 3 | 4 |
---|
**(4)**队头元素出队,输出5,同时令5的所有孩子(从左向右顺序)入队,5没有孩子,不操作,如图:
6 | 9 | 3 | 4 |
---|
**(5)**队头元素出队,输出6,同时令6的所有孩子(从左向右顺序)入队,如图:
9 | 3 | 4 | 7 |
---|
**(6)**队头元素出队,输出9,同时令9的所有孩子(从左向右顺序)入队,如图:
3 | 4 | 7 | 10 |
---|
**(7)**队头元素出队,输出3,同时令3的所有孩子(从左向右顺序)入队,3没有孩子,不操作,如图:
4 | 7 | 10 |
---|
**(8)**队头元素出队,输出4,同时令4的所有孩子(从左向右顺序)入队,4没有孩子,不操作,如图:
7 | 10 |
---|
**(9)**队头元素出队,输出7,同时令7的所有孩子(从左向右顺序)入队,如图:
10 | 8 |
---|
**(10)**队头元素出队,输出10,同时令10的所有孩子(从左向右顺序)入队,10没有孩子,不操作,如图:
8 |
---|
**(11)**队头元素出队,输出8,同时令8的所有孩子(从左向右顺序)入队,8没有孩子,不操作,如图:
此时队列为空,结束。输出顺序为1、2、5、6、9、3、4、7、10、8,该顺序即是BFS搜索的顺序,全图遍历。如果找最优解,搜索过程中找到便结束。
例题讲解
排位赛题Catch My Pet
假设小A起始位于点3,宠物狗位于点5,即N=3,K=5,最右边是6,如何搜索出一条最短路径呢?
1.深搜
从起点出发,随机挑一个方向,能往前走就往前走(拓展),走不动了则回溯。不能走已经走过的点(判重)
运气好的话:3->4->5或3->6->5
运气不好的话:3->2->4->5
运气最差的话:3->2->1->0->4->5
所以深搜就算找到了解,也不一定是最优解,当然也可以的,可以用各种手段优化的,比如,若已经找到路径长度为n 的解,则所有长度大于n的走法就不必尝试。但这样四处碰壁,效率上肯定就低了。
2.广搜
给节点分层。起点是第0层。从起点最少需n步就能到达的点属于第n层。
第1层:2,4,6
第2层:1,5
第3层:0
依层次顺序,从小到大扩展节点。
搜索过程(结点拓展过程):
3
2 4 6
1 5
问题解决。拓展时,不能拓展出已经走过的结点(要判重)
用队列来演示下:
1
2
#### 3
#### 4
#### 5
6
目标结点5出列,问题解决。
为什么结果一定是最优解呢?广搜虽然是全图遍历,但一旦着找到解就就会终止。
代码
#include<iostream>
#include<queue>
using namespace std;
struct Step
{
int x;
int steps;
Step(int xx=0,int ss=0):x(xx),steps(ss){}
};
queue<Step> q;
const int MAXN=100000;
int visited[MAXN]; //标记已走过的点
void bfs(int n,int k)
{
Step p1,p2;
p1.x=n;
p1.steps=0;
q.push(p1);
visited[n]=1;
while(!q.empty())
{
p1=q.front();//取出队首元素
if(p1.x==k)
{
cout<<p1.steps<<endl;
return;
}
else
{
//搜索符合的点,并加入到队列中
if(p1.x-1>=0&&!visited[p1.x-1])
{
p2.x=p1.x-1;
p2.steps=p1.steps+1;
q.push(p2);
visited[p1.x-1]=1;
}
if(p1.x+1<=MAXN&&!visited[p1.x+1])
{
p2.x=p1.x+1;
p2.steps=p1.steps+1;
q.push(p2);
visited[p1.x+1]=1;
}
if(p1.x*2<=MAXN&&!visited[p1.x*2])
{
p2.x=p1.x*2;
p2.steps=p1.steps+1;
q.push(p2);
visited[p1.x*2]=1;
}
q.pop();//抛出队首元素
}
}
}
int main()
{
int n,k;
cin>>n>>k;
bfs(n,k);
return 0;
}