leetcode:51. N 皇后
leetcode:[困难] 面试题 08.12. 八皇后
题目
n 皇后问题 研究的是如何将 n
个皇后放置在 n×n
的棋盘上,并且使皇后彼此之间不能相互攻击。
补充:皇后彼此之间不能相互攻击,指的是同一行 or 同一列 or 同一斜线上不能存在多个皇后
给你一个整数 n
,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q'
和 '.'
分别代表了皇后和空位。
示例 1:
输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,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) 的时间判断是否合法