前言
一个程序员一生中可能会邂逅各种各样的算法,不过搜索永远是其中最基础也是最重要的一种算法,下面我们就来分深度优先搜索和广度优先搜索来看一下吧。
深搜
深搜又叫深度优先搜索,dfs。顾名思义就是深度优先搜索,以深度第一,每一道搜索题其实都可以画出一棵搜索树。我们来举一个例子: 以这个求 1,2,3 的全排列为例,先枚举第一个数 1,再往下枚举出 1,2,3,但是明显 1 肯定不对,直接回溯,这就是剪枝,剪枝可以大大减少做题时的时间复杂度,再从 2 往下枚举 1,2,3再把 1,2排除,得到 3,输出后回到 2再回到 1,这就是回溯,回溯就代表着这条路径已经完结,要返回到上一个路口再一次进行选择,以此类推,就可以得到
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
然后我们来贴出一个最基本的全排列的算法,顺便让大家更好的理解深搜。
#include<bits/stdc++.h>
using namespace std;
int b[1000], a[1000], n;//b是标记,a是路径
void dfs(int x)
{
if(x==n+1)//结束条件
{//输出要求
cout<<" ";
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
if(i==n)
{
cout<<endl;
}
}
}
for(int i=1;i<=n;i++)
{
if(b[i]==0)//没有走过
{
a[x]=i;//统计路径
b[i]=1;//做好标记
dfs(x+1);//继续搜索
b[i]=0;//回溯标记
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
宽搜
宽搜又叫广度优先搜索或宽度优先搜索,bfs。宽搜需要使用队列queue来完成。那我们先来看一下宽搜的基本框架
while (队列不为空) {
读取头结点,头结点出队
扩展新结点 { // 状态转移
if (新结点可以入队) {
如果是目标结点,就地结束 // 若没有目标结点,则不判断
新结点入队
标记新结点已入队
}
}
}
- 状态表示:状态一般指对结点信息的描述,通常用T表示。一般用T0表示初始状态,Tn表示目标状态。
- 状态转移:根据产生式规则和约束条件控制从当前状态转移到下一个状态。
- 状态判重:大多数情况下,如果出现重复状态会造成死循环。这是宽度优先搜索不同于深度优先搜索的方面。
然后我们以一到走迷宫来详细解说一下宽搜。
农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0≤N≤100000)N(0≤N≤100000),牛位于点K(0≤K≤100000)K(0≤K≤100000)。农夫有两种移动方式:(这里要注意的是N可以到100000,所以数组要开到200000以上,(2*X))
1、从XX移动到X−1或X+1,每次移动花费一分钟
2、从X移动到2×X
,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?
以下就是代码了。
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int a[1000000];
int n,k;
struct node
{
int x,step;//结构体定义
};
node now,ne,start;
void bfs()
{
queue<node>que;
que.push(start);//入队
while(que.size())
{
now=que.front();
que.pop();
if(now.x==k)//抓住了
{
cout<<now.step;
return ;
}
for(int i=0; i<3; i++)//四个方向
{
if(i==0)//方向0
ne.x=now.x+1;
else if(i==1)//方向1
ne.x=now.x-1;
else if(i==2)//方向2
ne.x=now.x*2;
ne.step=now.step+1;
if(a[ne.x]==0&&ne.x>=0&&ne.x<100005)//是否越界
{
que.push(ne);
a[ne.x]=1;
}
}
}
}
int main()
{
cin>>n>>k;
memset(a,0,sizeof a);
a[n]=1;
start.x=n;
start.step=0;
bfs();
return 0;
}
结尾
那么到这里本篇对于搜索的介绍就到这里了,如果认为对你有帮助的,麻烦点一个赞吧。也请大家多多指教。