《代码随想录》Ⅶ 回溯算法-棋盘 51. N 皇后
努力学习!
题目:力扣链接
-
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将
n 个皇后放置在n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数
n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中
'Q' 和'.' 分别代表了皇后和空位。
一、思想
这道题的核心思想是使用回溯算法。回溯算法是一种通过递归尝试所有可能的解,并在发现当前解不符合条件时进行回退的算法。在N皇后问题中,我们需要在每一行放置一个皇后,并确保每一列和每一条斜线上都没有其他皇后。通过递归地尝试每一行的每一个可能的位置,并在放置皇后后检查是否满足条件,最终找到所有可能的解。
二、代码
class Solution
{
public:
/**
* 存储所有解的二维字符串向量
*/
vector<vector<string>> res;
/**
* 判断在当前行和列下是否可以放置皇后
* @param row 当前行
* @param col 当前列
* @param chessboard 当前棋盘状态
* @param n 棋盘的大小
* @return 如果可以放置返回true,否则返回false
*/
bool isVaild(int row, int col, vector<string> &chessboard, int n)
{
// 检查当前列是否有皇后
for (int i = 0; i < row; i++) {
if (chessboard[i][col] == 'Q') {
return false;
}
}
// 检查左上方向是否有皇后
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查右上方向是否有皇后
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
/**
* 回溯算法,尝试所有可能的放置位置
* @param n 棋盘的大小
* @param row 当前行
* @param chessboard 当前棋盘状态
*/
void backtracking(int n, int row, vector<string> &chessboard)
{
// 如果已经放置了n个皇后,则找到一个解,将其添加到结果中
if (row == n) {
res.push_back(chessboard);
return;
}
// 尝试在每一列放置皇后
for (int col = 0; col < n; ++col) {
// 如果当前位置可以放置皇后
if (isVaild(row, col, chessboard, n)) {
// 放置皇后
chessboard[row][col] = 'Q';
// 递归尝试下一行
backtracking(n, row + 1, chessboard);
// 撤销当前位置,尝试其他位置
chessboard[row][col] = '.';
}
}
}
/**
* 解决N皇后问题
* @param n 棋盘的大小
* @return 所有可能的解
*/
vector<vector<string>> solveNQueens(int n)
{
// 清空结果
res.clear();
// 初始化棋盘
vector<string> chessboard(n, string(n, '.'));
// 开始回溯
backtracking(n, 0, chessboard);
return res;
}
};
三、代码解析
1. 算法工作原理分解
1.1 回溯函数 backtracking
-
目的:递归地尝试在每一行放置皇后,并通过剪枝操作避免生成无效的解。
-
实现:
-
终止条件:
- 当
row 等于n 时,说明已经成功放置了n 个皇后,此时将当前棋盘状态chessboard 添加到结果集res 中。
- 当
-
选择与递归:
- 遍历当前行的每一列,尝试在每一列放置皇后。
- 如果当前位置
(row, col) 可以放置皇后(即通过isVaild 函数检查),则在棋盘上放置皇后,并递归地尝试在下一行放置皇后。
-
回溯:
- 在递归调用返回后,撤销当前位置的皇后,尝试其他可能的位置。
-
1.2 解决N皇后问题函数 solveNQueens
-
目的:初始化棋盘并调用回溯函数,生成所有可能的解。
-
实现:
-
初始化:
- 清空结果集
res。 - 初始化棋盘
chessboard,将其所有位置初始化为'.'。
- 清空结果集
-
调用回溯函数:从第0行开始调用
backtracking 函数,尝试在每一行放置皇后。 -
返回结果:返回生成的所有可能的解。
-
2. 关键点说明
2.1 路径的选择与回溯
- 路径:
chessboard 变量用于存储当前棋盘的布局。 - 选择:每次选择一个可以放置皇后的位置,并在棋盘上放置皇后。
- 回溯:在递归调用返回后,撤销当前位置的皇后,以便尝试其他可能的位置。
2.2 结果集的存储
- 结果集:
res 变量用于存储所有符合条件的棋盘布局。 - 添加条件:在每次递归调用开始时,如果
row 等于n,则将当前棋盘布局添加到res 中。
2.3 去重操作
- 去重:通过
isVaild 函数确保每一列和每一条斜线上都没有其他皇后,从而避免生成无效的解。
四、复杂度分析
-
时间复杂度:
O(N!)- 其中
N 是棋盘的大小。在最坏情况下,我们需要尝试每一行的每一个可能的位置,因此时间复杂度为O(N!)。
- 其中
-
空间复杂度:
O(N)- 递归调用栈的深度最多为
N,因此空间复杂度为O(N)。 - 结果集
res 的空间复杂度为O(N * N!),但在通常情况下,我们只考虑递归栈的空间复杂度。
- 递归调用栈的深度最多为
白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》

被折叠的 条评论
为什么被折叠?



