leetcode:51. N 皇后
leetcode:[困难] 面试题 08.12. 八皇后

题目

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

补充:皇后彼此之间不能相互攻击,指的是同一行 or 同一列 or 同一斜线上不能存在多个皇后

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

示例 1:
[困难] 51. N 皇后 & 面试题 08.12. 八皇后 - 图1

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

示例 2:

输入:n = 1
输出:[["Q"]]

解答 & 代码

递归回溯

class Solution {
private:
    vector<vector<string>> resultList;
    vector<string> curResult;

    /* 检测在第 row 行第 col 列摆放皇后是否合法 */
    bool isValid(int row, int col, int n)
    {
        // 0. 不需要检测行是否冲突,因为递归回溯就只给每行摆一个皇后
        // 下面三种检测,只需检测第 row 行之前是否存在冲突,因为递归回溯只摆放了 row 行之前 

        // 1. 检测列(上方)是否冲突
        for(int i = 0; i < row; ++i)
        {
            if(curResult[i][col] == 'Q')
                return false;
        }

        // 2. 检测 \ 对角线(左上方)是否冲突
        for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; --i, --j)
        {
            if(curResult[i][j] == 'Q')
                return false;
        }

        // 3. 检测 / 对角线(右上方)是否冲突
        for(int i = row - 1, j = col + 1; i >= 0 && j < n; --i, ++j)
        {
            if(curResult[i][j] == 'Q')
                return false;
        }

        // 不存在冲突,返回 true
        return true;
    }

    /* 递归回溯
       param n: 皇后总数,也是行数/列数
       param idx: 当前处理到第 idx 行,即摆放第 idx 个皇后。每行选一个位置放一个皇后
    */
    void backTrace(int n, int idx)
    {
        // 递归结束条件:如果已经摆放好 n 个皇后,则将结果存入 resultList 并返回
        if(idx == n)
        {
            resultList.push_back(curResult);
            return;
        }

        // 在第 idx 行的每一个位置尝试摆放皇后
        for(int j = 0; j < n; ++j)
        {
            // 选择:第 j 列的位置摆放皇后
            curResult[idx][j] = 'Q';
            // 如果当前位置摆上皇后不冲突,则递归回溯处理下一行
            if(isValid(idx, j, n))
                backTrace(n, idx + 1);
            // 撤销:将第 j 列的位置移除皇后
            curResult[idx][j] = '.';
        }
    }
public:
    vector<vector<string>> solveNQueens(int n) {
        string str(n, '.');
        curResult.resize(n, str);
        backTrace(n, 0);
        return resultList;
    }
};

复杂度分析:

  • 时间复杂度 O():每行放置一个皇后,每列也只能放一个皇后,第一行从 n 个位置种选一个放皇后,第二行就不能在同一列放皇后,因此第二行最多有 n-1 种选择,实际上再加上对角线也不能有多个皇后的限制,最终所有可能的情况少于 n!。而每个位置判断是否合法的时间复杂度 O(n),因此总的时间复杂度 O(n n!)
  • 空间复杂度 O(n):递归深度

执行结果:

执行结果:通过

执行用时:4 ms, 在所有 C++ 提交中击败了 90.99% 的用户
内存消耗:7.2 MB, 在所有 C++ 提交中击败了 67.49% 的用户

优化:

  • 空间换时间,用哈希表存储已经放了皇后的列号、\ 对角线编号(**行号-列号**/ 对角线编号(**行号+列号**,那么每次摆放能在 O(1) 的时间判断是否合法
  • 或者用 3 个 n 维数组,每次放置皇后,将对应位置标为 1,那么也能 O(1) 的时间判断是否合法

类似题目

[困难] 37. 解数独