题目
题目来源:N 皇后 - 力扣
分析
这道题的原型是大名鼎鼎的八皇后问题,事实上,除了暴力枚举以外,也很难有太好的方法来处理这个问题。而说到暴力枚举,回溯算法可以说是非常优秀的方法,那么这里我们用回溯的角度来思考一下应该如何完成这个问题。
下面以 4 x 4 的棋盘为例:
首先要知道皇后的攻击范围:
- 同行
- 同列
- 同一条对角线
以坐标 [1, 1],即 2 号位置为例,皇后攻击范围如图所示:
这些处于攻击范围的格子,它们有什么特点呢(设行为 row,列为 col)?
row相等col相等row + col相等row - col相等
结合上述几点,我们可以尝试遍历地图,并且找到不可下的位置:
function find(row, chessboard = []) {
for (let col = 0; col < n; col++) {
const cantSet = chessboard.some((ci, ri) =>
ri === row || ci === col || ri - ci === row - col || ri + ci === row + col
);
if (cantSet) continue;
find(row + 1, [...chessboard, col]);
}
}
其中值得注意的有:
chessboard中索引值为行,值为列ci, ri是之前摆放棋子的行列col, row为当前所在位置的索引
上述代码是如何完成回溯的呢?
其实仔细观察不难发现,当所有位置都不可下之后,循环会直接结束,回到上一层,即完成回溯,具体可以把值打印出来看看。
可见,当发现第二行所有位置都不能下载的时候,程序回退到了上一行继续进行尝试,到了这一步,整个查找基本已经完成,接下来就需要设置终止条件,相对来说,这个还是比较简单的:
- 什么时候终止呢?自然是
row === n的时候,因为在row === (n - 1)的时候,就已经是在找最后一行了。
而在终止的时候的操作也很简单,将棋盘转换成符合标准的形式,push 进结果集即可:
if (row === n) {
result.push(
chessboard.map(c => {
let arr = new Array(n).fill(".");
arr[c] = "Q";
return arr.join("");
})
);
}
最后将结果集返回即可:
var solveNQueens = function (n) {
const result = [];
find(0);
return result;
};
结果如下: