深度优先搜索的关键在于当下如何做,然后采用递归的方法,进行下一步的搜索,直到到达边界或者是其他的条件限制.
这里用一个简单的搜索案例引入:
要求输入一个数,然后输出1~n的全排列:
试想一下假如有3个数字即(n==3),
-
首先对于第一个位置,我们可以放1或2或3。 如果为1,然后手里剩余2和3,然后继续在第二个位置。
-
既可以放2,又可以放3,那么第二个位置,1就不可以再放了, 所以我们需要一个标记数组来记录该数字用没用,所以需要一个book数组。
-
然后第一个位置放完之后,我们需要在第二个位置放数字,由于这和第一次放数字是一个道理,所以这里用到了递归。不过位置变为了第二个位置,那么我们第二次调用的时候,就要去做一个判断,哪个数字用过了,就把它用对应的book数组标记为1,否则,才放数字。
-
一轮递归完成后,就说明第一次牌全部放完了,那么对应的牌应该收回,所以调用递归的后面应该将book数组变为0。
-
说到递归,那么首先要想到的应该是递归的结束条件,本题的结束条件应该为所走的位置比实际的大,即递归结束。
void dfs(int step)
{
int i;
if(step==n+1)
{
for(i=1;i<=n;i++)
{
printf("%d",a[i]);
}
printf("\n");
return ; //返回之前的一步(最近一次调用dfs函数的地方)
}
//在第step个盒子里面该放哪张牌?
//依次尝试
for(i=1;i<=n;i++)
{
if(book[i]==0)
{
a[step]=i;
//第i张牌已经放进第step个盒子里面了
book[i]=1;
//牌已经放进盒子里面了,不再手中了
dfs(step+1);
book[i]=0;
//因为有循环操作,所以要将牌收回;
}
}
return ;
}
以上就是代码部分,一定要注意递归结束的时候会有一个return,这个return会将函数退回到上一个递归调用的地方。
由以上可知,我们的dfs代码的基本步骤如下
void dfs(int step)
{
判断边界
尝试每一种可能 for(i=1;i<=n;i++)
{
继续下一步 dfs(step+1)
}
返回
}
我们趁热打铁,继续来看一看一个救人问题,当初看到很多方格里面要找一个人的坐标问题,当时感觉这个很奇妙,今天终于也有空闲时间学习这个了。
刚刚我们已经了解了深搜的代码部分,它最重要的就是要找到递归的边界以及当下如何做,然后以后的每一步和当下一样。
我们先来看看题目
小哈从起点开始,越过障碍物,到达终点,需要的最小步数,走一小格算一步
-
既然要在方格里走,那么每在一个地方,他就有最多4个方向可走,最少两个方向可走。
于是就要每次尝试一种走法,那么我们就需要一个方向数组next数组。 -
那么我们总不能把走过来又走回去吧,于是和上一道题目一样,我们需要一个book数组,这次我们需要记录的为走过的地点的坐标。
-
我们在地图中要做到怎样的判断才能够继续走下一步,首先我们需要考虑,所走的点有没有越界(即在地图外),其次我们还需要判断点的坐标是否已经走过了,以及是否为障碍物。
-
接下来需要想一想结束的条件,无非就是小哈所在点的坐标和终点坐标一样时,递归结束。
-
由于每次进行一次递归时,就会有一个总步数,我们只需将里面最小的值保留下来就行。
void dfs(int x,int y,int step)
{
int next[4][2] = {{0,1}//向右走
,{1,0}//向下走
,{0,-1}//向左走
,{-1,0}};//向上走
int tx,ty,k;
//判断是否为小哈的位置
if(x==p && y==q)
{
if(step<min)
{
//更新步数的最小值
min=step;
}
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]!=1 && book[tx][ty]!=1)
{
book[tx][ty]=1; //尝试该点
dfs(tx,ty,step+2);
book[tx][ty]=0; //尝试结束,收回该点
}
}
return ;
}
注意迷宫里面的x和y是这样子定义的
hc-hc
发布了6 篇原创文章 · 获赞 5 · 访问量 97
私信
关注