N皇后(N Queens)这样的全排列类型的DFS问题怎么在找到第一个解的时候就不再继续下去?
关注者
4被浏览
1,110登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏
给DFS增加一个终止条件(Terminator)就好了。
最近稍微研究了一下DFS,也问了一些人,题其实跟之前想的一样,就是增加一个终止条件让递归停下来。不过俗话说「人理解迭代,神理解递归」,我一开始无法理解增加一个终止条件会产生什么效果,比如会有「明明已经有了一个return了(在找到答案的时候),为什么还要增加一个return」这样的疑惑。
N Queens这样的for循环里的DFS可以用一个N * N的矩阵来模拟过程(类似的还有Combination Sum(39. Combination Sum ),Sudoku Solver等问题)。我简单画个图:
跟覃超上课提到的类似,递归(dfs)有点像《盗梦空间》,会有一层层的梦,梦醒来的时候会从内层往外层按照stack的顺序醒来。所以,途中找到一个答案之后,for循环移动到下一个格子发现不满足了,就回到上一层的梦。然后上一层再用for循环把皇后一次次右移。所以,如果我们增加一个terminator,在找到一个解之后就会不断地从底层的梦中醒来,不再执行下去。所以只要判断当前解集是否大于0就行了。
public void dfs(List<List<String>> result, int row, int n, int[] col) {
if (row == n) {
List<String> cell = new ArrayList<>();
//打印一个解
for (int i = 0; i < row; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < row; j++) {
if (col[i] == j) {
sb.append("Q");
} else {
sb.append(".");
}
}
cell.add(sb.toString());
}
result.add(new ArrayList<String>(cell));
return;
}
if (result.size() > 0) return;
for (int i = 0; i < n; i++) {
//i表示Q所在的列 从头到尾遍历一遍
col[row] = i;
if (checkValid(row, col)) {
dfs(result, row + 1, n, col);
}
}
}
@LoveYayoi 的想法也是类似的,就是给dfs一个返回值类型。如下:
public boolean dfs(List<List<String>> result, int row, int n, int[] col) {
if (row == n) {
List<String> cell = new ArrayList<>();
//打印一个解
for (int i = 0; i < row; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < row; j++) {
if (col[i] == j) {
sb.append("Q");
} else {
sb.append(".");
}
}
cell.add(sb.toString());
}
result.add(new ArrayList<String>(cell));
return true;
}
for (int i = 0; i < n; i++) {
col[row] = i;
if (checkValid(row, col)) {
if (dfs(result, row + 1, n, col)) return true;
}
}
return false;
}
更详细的分析可以看我的blog: 52. N-Queens II ,全排列类型DFS的理解
知乎的编辑器好难用哦。。烦 。能用markdown吗
-