1. DFS的重复遍历
对DFS而言,必然是需要标记数组的,也就是当前单位是否被遍历过,否则DFS将不会停止。但有一个非常特别的不同,就是当前单位是否被遍历过是对于下层单位来说的,还是对于全局来说的,这将取决于题目的性质,也决定了标记数组不同的使用方法,下面将通过举两个例子来说明这个点。
2. N皇后问题
2.1 题目分析
很明显的一道回溯的题目,这里我们假设已经放了k-1个皇后,对于第k个的放置,应该在第k+1行(因为数组自0开始计数),通过遍历第k+1行,如果有满足条件的,我们就将第k个皇后放上去,重复这个过程,直到放置完成,将这个放置方案输出。
后面是重点:我们如何去存储皇后放置的位置,这也决定了是否具有标记的必要:
2.2 第一种存储方案
我们将定义一个map[n][n]
的数组,对于放置皇后的位置赋值为1,传进来行列i,j
先判断当前是否满足条件,如果满足就赋值为1,传入i+1,0
继续进行上述过程,不满足我们将i,j+1
传入。
如果对于某一行,任何一列都不能满足要求,这就说明前面的放置出现问题,而前面的放置已经导致map的值发生改变,这个时候对于这一行来说,便不能够具有“纠错”的功能了,所以这里我们将使用这样的语句:
if(ij条件满足)
{
map[i][j]=1;//(2)
传入(i+1,0);//(3)
map[i][j]=0;//(4)
}
由此当(3)没有满足条件的时候(或者全部执行结束的时候),(4)语句将会执行,将放置到这个位置的皇后拿走,为后面的遍历做准备,这时的(2)语句更多地是为了对从属它的搜索进行限制,而不对同一层的搜索进行限制。(如果没有(4),第i行只能将皇后放在这个位置而不能改变了,从而也就不能得出所有的解)
2.3 第二种存储方案
代码如下:
private void check(int n){
if(n==max) //n==max时,说明前max个皇后已经放好了
{
print();
ans++;
return;
}
//依次放入皇后,并判断是否冲突
for(int i=0;i<max;i++)
{
//先把当前的皇后放到该行的第i列
arr[n]=i;
//判断这个放置有没有问题,
if(judge(n))
//放置n+1皇后
check(n+1);
}
}
private boolean judge(int n){//n表示第n+1个皇后,那么arr[n]就是第n+1行这个皇后所处的列数
for(int i=0;i<n;i++){
if(arr[i]==arr[n]||Math.abs(n-i)==Math.abs(arr[n]-arr[i]))//表示同一斜线,其实是行差绝对值等于列差绝对值
return false;
}
return true;
}
其中arr[n]=i表示第n+1个皇后放在了第i列,第n+1行。而因为遍历和皇后问题的关系,并不需要标识,因为使用了遍历的方法,对于每一行来说,皇后只能有一个,这样对于同一行元素没有必要甄别,也就没必要标识了。
3. 岛屿的最大面积
3.1 问题分析
对于这个问题,我们使用如下的方法:
public int dfs(int i,int j,int[][] grid,int[][] flag){
int dr[]={-1,0,1,0};
int dl[]={0,-1,0,1};
if(i>=0&&i<grid.length&&j>=0&&j<grid[0].length&&flag[i][j]==0&&grid[i][j]==1){
flag[i][j]=1;//(6)
int ans=1;
for(int a=0;a<4;a++){
// flag[i][j]=1;//(7)
ans=ans+dfs(i+dr[a],j+dl[a],grid,flag);
// flag[i][j]=0;//(8)
}
return ans;
}
else
return 0;
}
这里如果我们使用(7)(8)而不是(6)的话,这会导致标识数组对于全局没有影响力,而对于这道题,将会导致某些单位被重复计数,尽管在连贯的搜索中其不会出现重复计数。我们将用一个图来表示:
欢迎来我的博客看看