LeetCode-51-N皇后
in LeetCode with 0 comment

LeetCode-51-N皇后

in LeetCode with 0 comment

原题地址:N皇后

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

8-queens

上图为 8 皇后问题的一种解法。

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

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

示例:

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

回溯算法

这是回溯算法的一个典型应用。在国际象棋中皇后的攻击范围是所在的直线和对角线,所以本题实际上是要求将 n 个皇后放置在 n×n 的棋盘上,并且使所有的皇后相互之间都不能在同一条直线和对角线上。

所以我们求解的过程就是依次放皇后,每次都任选一个有效的位置放置,当发现某一步的皇后没有位置可放时就说明这个方案是失败的,此时就回退选择其它方案,直到 n 个皇后都放到了棋盘上,就证明找到了有效的方案。

具体的实现方式如下:

/**
 * @param {number} n
 * @return {string[][]}
 */
const solveNQueens1 = function(n) {
    // 记录皇后的位置,下标为行,值为列
    let queen = new Array(n).fill(-1);
    let result = [];

    /**
     * 判断新的皇后放在某一行是不是有效的
     * @param row 行数
     * @returns {boolean} 是否合法
     */
    const isOk = (row) => {
        // 依次判断已经放置的皇后是否在同一列或同一对角线上
        for (let i = 0; i < row; i ++) {
            if (queen[i] === queen[row] || // 同一列有两个皇后
                row - queen[row] === i - queen[i] || row + queen[row] === i + queen[i]) { // 对角线
                return false;
            }
        }
        return true;
    };

    /**
     * 放置某一行的皇后
     * @param row 行号
     */
    const placeQueen = (row) => {
        // n个皇后已经放置完毕,方案可行,将该方案加入结果集
        if (row === n) {
            result.push([...queen]);
        } else {
            // 依次尝试放皇后放在每一列,判断该方案是否OK,如果OK就继续放置下一行
            // 否则证明该方案不可行,剪枝
            for (let column = 0; column < n; column ++) {
                queen[row] = column;
                if (isOk(row)) {
                    placeQueen(row + 1);
                }
            }
        }
    };
    // 从第一行开始放置皇后
    placeQueen(0);
    // 需要将结果转换成题目要求的方式
    return result.map(item => {
        let array = new Array(n);
        for (let i = 0; i < n; i ++) {
            let str = '';
            for (let j = 0; j < n; j ++) {
                str += item[i] === j ? 'Q' : '.'
            }
            array[i] = str;
        }
        return array;
    });
};

测试:

let start = new Date();
const test = solveNQueens1;
/**
 [
     [ '.Q..', '...Q', 'Q...', '..Q.' ],
     [ '..Q.', 'Q...', '...Q', '.Q..' ]
 ]
 */
console.log(test(4));
/**
 [
     [ 'Q....', '..Q..', '....Q', '.Q...', '...Q.' ],
     [ 'Q....', '...Q.', '.Q...', '....Q', '..Q..' ],
     [ '.Q...', '...Q.', 'Q....', '..Q..', '....Q' ],
     [ '.Q...', '....Q', '..Q..', 'Q....', '...Q.' ],
     [ '..Q..', 'Q....', '...Q.', '.Q...', '....Q' ],
     [ '..Q..', '....Q', '.Q...', '...Q.', 'Q....' ],
     [ '...Q.', 'Q....', '..Q..', '....Q', '.Q...' ],
     [ '...Q.', '.Q...', '....Q', '..Q..', 'Q....' ],
     [ '....Q', '.Q...', '...Q.', 'Q....', '..Q..' ],
     [ '....Q', '..Q..', 'Q....', '...Q.', '.Q...' ]
 ]
 */
console.log(test(5));
console.log(new Date().getTime() - start.getTime()); // 11

时间复杂度: 放置第一个皇后有n种方式,放置第二个皇后不超过n-1种方式,……,放置第m个皇后不超过n-m+1种方式,所以总的时间复杂度为$O(n!)$
空间复杂度: 递归的栈不超过n,空间复杂度为$O(n)$