LeetCode 51 N-Queens (Tag:Array Difficulty:Hard)

202 阅读3分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

前言

关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!

题目描述

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。(皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。)

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

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

示例 1: image.png 输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2: 输入:n = 1
输出:[["Q"]]

链接:leetcode-cn.com/problems/n-…

题解

这道题目和LeetCode 37 数独是类似的, 都是采用 dfs+回溯 的方法来填充空格.

本题解法有几个重点.
一个是标记数组: 共需要三个标记数组, col, diag1, diag2, 分别表示列, 红色对角线, 蓝色对角线, 如果数组对应 index 值为 1, 表示第 index 列/对角线上放置了皇后, 该位置不能再放置皇后. 这里不需要对行进行标记, 因为我们是从上到下进行遍历的, 行肯定是不会重复放置皇后的. 同时对于 diag1 和 diag2 来说, 在斜对角线上的元素坐标有以下的特点, 一共各有 2n - 1 个斜线; 红对角线上的元素 i + j 即行 index 和列 index 相加的值是一样的; 蓝色对角线上元素 i - j 的值是一样的, 因为 i - j 会存在负数, 为了便于用数组的下标表示, 用 i - j + n - 1 来对应在 diag2 的下标.
image.png (图片来源于 花花酱)

第二个是递归终止条件, 和 leetcode 37 题一致, 当遍历到行数超过 n 时, 说明 0 - n-1 行已经填好了.

第三个是回溯, 在递归调用之后回溯时, 要将棋盘还原, 方便后续的递归调用.

具体代码见下方

时间复杂度 O(n!), 因为第一行有 n 种情况可以选择, 第二行有 n - 1种,..., 因此 n 行一共需要遍历判断 n! 种情况.

空间复杂度 O(n). 递归深度最高为 n.

/**
 * @param {number} n
 * @return {string[][]}
 */
var solveNQueens = function(n) {
    let res = []
    let board = Array.from({ length: n }, () => (new Array(n).fill('.')))
    let col = new Array(n).fill(0)
    let diag1 = new Array(2 * n - 1).fill(0)
    let diag2 = new Array(2 * n - 1).fill(0)
    
    // 判断该位置是否能够放置皇后
    const available = (x, y) => {
      return !col[y] && !diag1[x + y] && !diag2[x - y + n - 1]
    }
    
    // 放置皇后或者恢复棋盘
    const update = (x, y, input) => {
      col[y] = input
      diag1[x + y] = input
      diag2[x - y + n - 1] = input
      board[x][y] = input ? 'Q' : '.'
    }
    
    const dfs = (x) => {
      // 递归终止条件
      if (x === n) {
        res.push(board.map(row => row.join('')))
        return
      }
      
      // 从 0 行开始填充棋盘
      for (let i = 0; i < n; ++i) {
        if (available(x, i)) {
          update(x, i, 1)
          dfs(x + 1)
          // 回溯
          update(x, i ,0)
        }
      }
    }
    
    dfs(0)
    return res
};