题目要求点击查看原题
题目分析
观察上图可以知道要满足皇后之间彼此不能攻击需要满足下面四个条件
1:不在同一排
2:不在同一列
3:不在斜率为 1 的同一直线上(/)
4:不在斜率为 -1 的同一直线上(\)
因为是要找到所有摆放的情况,就要一个个位置试探着去摆放自然而然就会想到回溯法
一开始我计划用一个二维数组map 来存放皇后的摆放位置,和禁止摆放位置以n=4为例
[
['Q','.','.','.'] // Q 代表皇后的位置
['.','.','1','1'] // . 代表不能摆放的位置
['.','1','.','1'] // 1 代表能摆放的位置
['.','1','1','.']
]
// 既然用到了标准位了,为什么不用二进制来表示呢?
// 所以上面的map可以转换为==》
[
[1,0,0,0] // 1
[0,0,1,1] // 12
[0,1,0,1] // 10
[0,1,1,0] // 6
]
//1代表皇后的位置或者是可以放皇后的位置
// 将每个数组看做是一个4位的二进制数 转换为10进制数就是 ==>
[1,12,10,6] // 这样我们就可以将原本的操作二维数组改成操作一维数组了。
上代码(解析在注释中)
var solveNQueens = function(n) {
// 特殊值 1 的时候只有一个解 [['Q']]
// n < 4 的时候无解(可以自己在纸上画一画)
if (n === 1) {
return [["Q"]];
}
let res = [];
let max = Math.pow(2, n) - 1;
for (let i = 0; i < n; i++) {
// 初始化地图 例n = 4 =》 [15,15,15,15];
// 循环填充第一排的皇后,在结果查找结构
find(fillMap(new Array(n).fill(max), 0, i), 1);
}
return res;
// 完成填充map的功能
function fillMap(map, y, x) {
let h = Math.pow(2, x);
// 嚣张的一次性解决掉一排 比如将皇后放在第3个位置 =》 [0,0,1,0] = 4
map[y] = h;
// i的初始值为y+1 ==> 即计算下一排需要置零的位置 不在同列和斜线上
for (let i = y + 1; i < n; i++) {
// 同列是必须要置零的
let t = h;
// k = 1 ; ==》 根据公式y - yo = k(x-xo) 将x,y 和 k= 1代入可以计算出
let positiveX = i - y + x;
// 位置必须在地图内 即 0 < 计算的x < n
if (positiveX >= 0 && positiveX < n) {
t += Math.pow(2, positiveX);
}
// k = -1 同 k = 1
let negativeX = x + y - i;
if (negativeX < n && negativeX >= 0) {
t += Math.pow(2, negativeX);
}
// t 相当于每个要置零的位置 max - t 相当于取反
// 这里必须要用位操作,不能直接减 ,因为一个位置可能被多次置零。
map[i] &= max - t;
}
return map;
}
// 完成回溯查找的功能
function find(map, y) {
// 结束条件 如果填充到最后一排了要么是0 要么是 2^X
if (y === n - 1) {
if (map[y] !== 0) {
res.push(
// 将一维数组转换为实际的结果
map.map(item => {
let x = Math.log2(item);
return ".".repeat(x) + "Q" + ".".repeat(n - 1 - x);
})
);
}
return;
}
// 值不为0 代表这一排有可以放皇后的位置
while (map[y] !== 0) {
// 可以直接计算出可以放皇后的最高位,
let x = Math.floor(Math.log2(map[y]));
find(fillMap([...map], y, x), y + 1);
// 只要一次查找完结,无论成功还是失败 都需要将这个位置置零开始下一次试探。
map[y] -= Math.pow(2, x);
}
//如果某一排为0 代表这一排没有可以放皇后的位置试探结束
}
};