一、题目描述
n 皇后问题 研究的是如何将 n
个皇后放置在 n × n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n
,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4 输出:2 解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1 输出:1
提示:
1 <= n <= 9
二、解题思路
- 初始化一个布尔型二维数组
board
,表示棋盘,board[i][j]
表示第 i 行第 j 列是否可以放置皇后。 - 创建一个递归函数
dfs
,该函数尝试在当前行放置一个皇后,并递归地在下一行尝试放置下一个皇后。 - 在
dfs
函数中,遍历当前列的每一个位置,如果当前位置可以放置皇后(即board[i][j]
为true
),则放置皇后,并递归地调用dfs
函数尝试在下一行放置下一个皇后。 - 如果在当前行找不到可以放置皇后的位置,就回溯到上一行,移动皇后到下一个位置。
- 每次成功放置一个皇后,计数器加一,表示找到了一个有效的解。
- 最终,返回计数器的值,即为所有可能的解的数量。
三、具体代码
class Solution {
private int count = 0; // 用于记录解的数量
public int totalNQueens(int n) {
boolean[][] board = new boolean[n][n]; // 初始化棋盘
dfs(0, board); // 从第一行开始递归放置皇后
return count; // 返回解的数量
}
private void dfs(int row, boolean[][] board) {
if (row == board.length) { // 如果到达最后一行,说明所有皇后都已放置完毕
count++; // 解的数量加一
return; // 递归结束
}
for (int col = 0; col < board.length; col++) { // 遍历当前列的每一行
if (isValid(row, col, board)) { // 如果当前位置可以放置皇后
board[row][col] = true; // 放置皇后
dfs(row + 1, board); // 递归到下一行
board[row][col] = false; // 回溯,移除皇后
}
}
}
private boolean isValid(int row, int col, boolean[][] board) {
// 检查同一列是否有皇后
for (int i = 0; i < row; i++) {
if (board[i][col]) return false;
}
// 检查左上对角线是否有皇后
for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {
if (board[i][j]) return false;
}
// 检查右上对角线是否有皇后
for (int i = row, j = col; i >= 0 && j < board.length; i--, j++) {
if (board[i][j]) return false;
}
return true; // 如果都没有冲突,当前位置可以放置皇后
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 每行有 n 个位置可以放置皇后,所以每行的尝试次数是 n。
- 对于每一行的每个有效位置,我们需要检查该位置是否可以放置皇后,这涉及到检查同一列、左上对角线和右上对角线上是否有其他皇后。在最坏的情况下,这三个检查都需要线性时间来完成(即 O(n)),因此每次放置的复杂度是 O(n)。
- 总的时间复杂度是每行尝试次数与每次尝试的复杂度的乘积,即 O(n^2)。
- 但是,由于我们在每一行的尝试过程中会跳过很多无效的位置,实际的时间复杂度通常比这个更小。
- 实际上,时间复杂度更接近于 O(n!),因为每个皇后的放置都依赖于前面的皇后放置位置。
2. 空间复杂度
- 我们使用了一个 n x n 的布尔型二维数组
board
来存储棋盘的状态,因此空间复杂度是 O(n^2)。 - 递归栈的深度取决于递归的层数,最坏情况下,递归深度为 n,即所有行都尝试放置皇后。
- 因此,递归栈的空间复杂度是 O(n)。
五、总结知识点
1. 回溯算法(Backtracking):
- 回溯算法是一种通过递归来试探和回溯的算法,它尝试分步解决一个问题。如果发现当前步骤不能得到有效的解或者达到问题的某个约束条件,它将取消上一步或几步的计算,再通过其他可能的分步解决方案继续尝试。
- 在 n 皇后问题中,回溯算法用于尝试在棋盘的每一行放置一个皇后,并递归地继续在下一行放置下一个皇后,直到所有皇后都放置完毕或者发现当前放置的皇后与已有的皇后有冲突。
2. 位运算(Bit Manipulation):
- 代码中没有直接使用位运算,但位运算是解决 n 皇后问题的另一种高效方法。通过将棋盘的每一行和每一列以及两个对角线的状态用三个整数的二进制位来表示,可以快速地检查放置皇后的位置是否有效,以及在放置和移除皇后时进行快速更新。
3. 数组和布尔逻辑:
- 使用二维布尔数组
board
来表示棋盘,其中true
表示对应位置可以放置皇后,false
表示不可放置。 - 通过布尔逻辑来检查当前位置是否有效,即同一列、左上对角线和右上对角线是否有其他皇后。
4. 递归(Recursion):
- 递归是一种通过函数自己调用自己来重复执行代码的方式。在这段代码中,
dfs
函数递归地尝试在棋盘上放置皇后,并在找到所有解后返回。
5. 迭代与三重循环:
- 代码中的
for
循环用于迭代棋盘的每一列,以及在isValid
函数中迭代检查同一列、左上对角线和右上对角线是否有冲突的皇后。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。