C.深搜与广搜的基础代码实现
下面以一道简单的搜索算法题为例:
在一个,迷宫中寻找出口,并且要求路径最短。且迷宫中有障碍物不能穿过,并由规定起点出发。输入包括三部分,第一行:迷宫的尺寸大小。
第二行:定义迷宫。 第三行:起始位置和出口所在。
深搜代码实现部分:
#include<stdio.h>
int n,m,p,q,min=9999;
int a[51][51],book[51][51];//a用来储存迷宫,book用来记录已走过的路径。
int sum;//记录所有出口的方式。
void dfs(int x,int y,int step);//深搜函数。
int main()
{
int i,j,startx,starty;
scanf("%d %d",&n,&m);//输入迷宫大小。
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);//编辑迷宫。
scanf("%d %d %d %d",&startx,&starty,&p,&q);//初始坐标和出口赋值。
book[startx][starty]=1;//初始坐标已走过避免再次搜索。
dfs(startx,starty,0);//传递参数。
printf("%d\n",min);
printf("%d\n",sum);
system("pause");
return 0;
}
void dfs(int x,int y,int step)
{
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//分别定义上下左右四个方向。
int tx,ty,k;//[tx][ty]表示下一步坐标,k为了遍历所有方向
if(x == p && y == q)//判断是否为出口
{
if(step < min)
{
min = step;//更新最短路径
}
sum++;//找到出口sum+1
return;
}
for(k=0;k<=3;k++)
{
tx = x + next[k][0];
ty = y + next[k][1];
if(tx < 1 || tx > n || ty < 1 || ty > m)//如果越界不再进行下一步,直接寻找其他未越界的方向
{
continue;
}
if(a[tx][ty] == 0 && book[tx][ty] == 0)//判断该方向是否可以继续行走
{
book[tx][ty] = 1;//记录当前坐标已走过
dfs(tx,ty,step+1);//在当前基础上进行下一步搜索
book[tx][ty] = 0;//遍历过该方向便收回以方便其左边搜索时可以行走
}
}
return;
}
以上为深搜的实现代码,带有注释应该是不难理解的。那么下面就来看看广搜吧,并从中发现他们的不同所在吧。
广搜代码实现部分:
#include<stdio.h>
struct note//队列
{
int x;//横坐标
int y;//纵坐标
int f;//父节点(上一个结点)
int s;//步数
};
int main()
{
struct note que[2501];//定义队列
int a[51][51]={0},book[51][51]={0};//定义迷宫数组,book用于记录哪些点已遍历过
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//分别代表向右,想下,向左,向上
int head,tail;//head表示正扩展的结点,tail位于队尾最后一个元素的后面随时将head扩展出来的结点入队
int i,j,k,n,m,startx,starty,p,q,tx,ty,flag;
scanf("%d %d",&n,&m);//迷宫规格
for(i = 1;i <= n;i++)
for(j = 1;j <= m;j++)
scanf("%d",&a[i][j]);
scanf("%d %d %d %d",&startx,&starty,&p,&q);//起点,终点
head = 1;
tail = 1;
//将初始入口入队
que[tail].x = startx;
que[tail].y = starty;
que[tail].f = 0;
que[tail].s = 0;
tail++;//每执行一次入队就后移一位
book[startx][starty] = 1;//起点已到达过
flag = 0;//如找到终点即flag=1,现在表示未找到终点
while(head < tail)//队列不为空
{
for(k = 0;k < 4;k++)//枚举4个方向
{
tx = que[head].x + next[k][0];
ty = que[head].y + next[k][1];
if(tx < 1||tx > n||ty < 1||ty > m)//判断是否越界
continue;
if(a[tx][ty] == 0 && book[tx][ty] == 0)//判断是否为障碍,是否已经走过
{
book[tx][ty] = 1;//记录当前节点已走过,广搜每个节点只搜索一次,所以之后不需要再次置零
que[tail].x = tx;
que[tail].y = ty;
que[tail].f = head;
que[tail].s = que[head].s + 1;
tail++;//没入队一次tail++
}
if(tx==p && ty==q)//是否找到出口
{
flag = 1;
break;
}
}
if(flag == 1)
break;
head++;//当前节点的扩展未有找到出口,搜索下一个节点
}
printf("%d\n",que[tail - 1].s);//因为tail指向队尾最后一个元素的后一位
system("pause");//程序运行完毕,暂停检查数据
return 0;
}
以上就是两种搜索的基本实现,从中不难看出:
深搜是属于“不撞南墙不回头”?这么说应该对的哈。总之,深搜大抵就是,有一个结点开始,选择一条分支进行深搜,当遇到下一结点重复上述步骤,知道这一分支被搜索完毕,然后再从搜索完的最下面的结点,向上回溯,如果还有其他选择,在对其他选择进行搜索。大体流程就是这样,从树的根部搜索到最末尾的分支,然后,一个节点搜索完毕后,回溯,再搜索,知道在回溯到树的根部,再由树的根部发出另一分支。搜索到目标则停止。
广搜就有别于深搜,广搜是向外发散式的搜索。就像Wi-Fi的图标,大家都是熟悉的吧,由树的根部也就是Wi-Fi的那个点一层一层的向外发散,搜索完一层,再进入下一层,直至到达最后一层。相对于深搜来说,他更显得盲目。但却并不花费更多时间,相对来说,深搜更加浪费时间。
写在最后
搜索题大体都是这样的模板,如果遇到问题仔细考虑,判断用什么搜索方法。祝愿各位奋斗在C代码海洋的同胞们有心人终成正果。
泪目°