【每日算法】N皇后

66 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

N皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

示例

输入: n = 4

输出:

[
  [
    ".Q..",
    "...Q",
    "Q...",
    "..Q."
	],
  [
    "..Q.",
    "Q...",
    "...Q",
    ".Q.."
  ]
]

思路

根据示例来寻找一些规律

  • 先看输出中的第一个数组,锁定第一条数据中的 Q 的索引位置是 0, 1
    • 0, 1 时,右侧斜线(同一斜线可攻击,故而不能放置其他的 Q)位置是: 1,22,3
    • 可以发现行 - 列 是一样的
  • 第二个数组,第一条数据中 Q 的索引是 0, 2
    • 左边斜线的位置是: 1,10,2
    • 发现 行 + 列 是一样的

所以我们目前发现的限制条件是

  1. 行不能一样
  2. 列不能一样
  3. 行 - 列 不能一样
  4. 行 + 列 不能一样

代码实现

var solveNQueens = function(n) {
  let ret = [] // 结果存放数组
  find(0) // 从第1行(索引为0)开始查找
  return ret;
  
  // tmp的作用:记录之前的路径
  // tmp的索引是行数据:tmp是一个数组,数组中每个元素的值都记录的是列索引,数组本身位置的索引记录的是行索引
  // 例如:tmp = [1, 3, 0, 2] 代表 "0,1"、"1,3"、"2,0"、"3,2" 这四个地方存放的 Q
  // row 代表当前行
  function find(row, tmp = []) {
    if(row === n) {
      // 找完了 n-1 就已经是最后一行了(一共n行),tmp就是本次的摆放位置
      ret.push(tmp.map(c => {
        let arr = new Array(n).fill(".")
        arr[c] = "Q"
        return arr.join("")
      }))
    }
    // 逐列查找,在当前行中依次遍历每一个元素(这里已经排除了行相等的情况了)
    for(let col=0; col<n; col++) {
      // 是否不能放置
      let cantSet = tmp.some((ci, ri) => {
        // ci和ri是之前摆放棋子的行列索引
        // col和row是当前所在位置的索引
        return ci === col                // 列相同(此列已经存放过其它Q了)
               || (ri-ci) === (row-col)  // 行-列相同(右侧斜线已经有其他Q了)
               || (ri+ci)===(row+col)	 // 行+列相同(左侧斜线已经有其他Q了)
      })
      if(cantSet) continue // 本行的当前列不能放,继续本行的下一列
      // 如果能放置,则将列放进 tmp 中,继续下一行
      find(row+1, [...tmp, col])
    }
  }
}