前言
「N 皇后问题」研究的是如何将 N 个皇后放置在 N × N 的棋盘上,并且使皇后彼此之间不能相互攻击。
皇后的走法是:可以横直斜走,格数不限。因此要求皇后彼此之间不能相互攻击,等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。
LeetCode 链接 51.N皇后
接下来我介绍一种基于集合的回溯解法
基于集合的回溯
回溯算法是一种遍历算法,以深度优先遍历 的方式尝试所有的可能性。有些教程上也叫「暴力搜索」。回溯算法是 有方向地 搜索,区别于多层循环实现的暴力法。
思路分析
以 4 皇后问题为例,它的「搜索」过程如下。大家可以在纸上模拟下面这个过程:
搜索的过程蕴含了 剪枝 的思想。「剪枝」的依据是:题目中给出的 「N 皇后」 的摆放规则:1、不在同一行;2、不在同一列;3、不在同一主对角线方向上;4、不在同一副对角线方向上。
利用一个数组记住皇后的位置
考虑对角线(找规律)
在每一个单元格里写下行和列的 下标。会发现主对角线上的元素 ( 横坐标 - 纵坐标 )的值相等,副对角线上的元素( 横坐标 + 纵坐标 ) 的值相等
参考代码
/**
* @param {number} n
* @return {string[][]}
*/
var solveNQueens = function (n) {
const solutions = []
dfs(solutions, 0, [], new Set(), new Set(), new Set(), n)
return solutions
};
/**
* @param {string[][]} solutions 所有解
* @param {number} row 当前搜索行
* @param {number[]} path 存储皇后的数组
* @param {set} columns 存在皇后的列集合
* @param {set} diagonal1 存在皇后的主对角线集合
* @param {set} diagonal2 存在皇后的副对角线集合
* @param {number} n 皇后总数
* @return {void}
*/
// 因为是深度优先 所以皇后占用的 rows 这个集合被省略,深度优先算法在每一行一旦找到一个皇后会进入下一行探索
function dfs(solutions, row, path, columns, diagonal1, diagonal2, n) {
// 到了最后一行任然满足条件则会继续调用下一行 此时 row === n 说明所有皇后已经放置完毕
if (row === n) {
//将结果保存在 solutions 数组内
solutions.push(createBoard(path, n))
return
}
// 搜索当前行的每一列是否可以放置皇后
for (let i = 0; i < n; i++) {
// 判断是否满足当前坐标所在的 行、列、两个对角线均不存在皇后的条件,不满足条件的分支则被裁剪不会继续探索下一个深度
if (!columns.has(i) && !diagonal1.has(row - i) && !diagonal2.has(row + i)) {
// 满足条件后放置皇后并且记录新放置皇后占用的列和对角线
path.push(i)
columns.add(i)
diagonal1.add(row - i)
diagonal2.add(row + i)
// 搜索下一行
dfs(solutions, row + 1, path, columns, diagonal1, diagonal2, n)
// 深度优先算法调用完毕后已经完成了当前列的所有尝试,满足条件的结果已经被保存进了 solutions 数组内,尝试下一列就需要去掉当前列所在皇后占用的列和对角线
// 回溯
const oldColumn = path.pop()
columns.delete(oldColumn)
diagonal1.delete(row - oldColumn)
diagonal2.delete(row + oldColumn)
}
}
}
/**
* @param {number[]} path 保存皇后的路径
* @return {string[][]} 棋盘
*/
function createBoard(path, n) {
const board = []
for (let i = 0; i < path.length; i++) {
let item = ''
for (let j = 0; j < n; j++) {
if (j === path[i]) {
item += 'Q'
} else {
item += '.'
}
}
board.push(item)
}
return board
}