N 皇后问题回顾
N 皇后问题的目标是将 N 个皇后放置在 N×N 的国际象棋棋盘上,确保没有两个皇后能相互攻击。皇后的攻击方式是横向、纵向以及对角线。也就是说,在棋盘上,任意两个皇后不能处于相同的行、相同的列或同一条对角线上。
回溯算法的步骤详细说明
回溯算法是一种通过递归搜索所有可能解的算法,尤其适合解决排列组合和满足条件的搜索问题。在 N 皇后问题中,我们从第一行开始放置皇后,并尝试在每一行放置一个皇后,直到所有皇后都放置完毕(即达到了 N 行)。如果当前行不能合法放置皇后,我们回溯到上一行并尝试其他列的位置。
核心思想
- 逐行放置皇后:从第 1 行开始,依次为每一行放置一个皇后。每一行都会尝试将皇后放置在不同的列中。
- 合法性检查:每放置一个皇后时,必须检查其是否与之前已经放置的皇后发生冲突。具体来说,不能和其他皇后处于同一列或同一对角线上。
- 回溯:如果在某一行找不到合适的列来放置皇后,则说明当前的部分解不合法,需要“回溯”到上一行,尝试改变上一行的皇后位置,直到找到新的可能解。
- 递归终止条件:当皇后成功放置到所有 N 行时,表示找到一个完整的解。此时,将该解保存下来,并继续搜索其他可能的解。
详细 Java 实现
import java.util.ArrayList;
import java.util.List;
public class NQueens {
// 主方法,解决 N 皇后问题
public List<List<String>> solveNQueens(int n) {
List<List<String>> solutions = new ArrayList<>(); // 用于存储所有的解决方案
int[] queens = new int[n]; // 用于记录每一行皇后放置的列索引
for (int i = 0; i < n; i++) {
queens[i] = -1; // 初始化时,所有行的皇后均未放置,设为 -1
}
backtrack(solutions, queens, n, 0); // 从第 0 行开始递归放置皇后
return solutions;
}
// 回溯方法,逐行尝试放置皇后
private void backtrack(List<List<String>> solutions, int[] queens, int n, int row) {
// 当 row 等于 n 时,说明已经成功将 n 个皇后放置在 n 行,找到一个解
if (row == n) {
solutions.add(generateBoard(queens, n)); // 将解转换成棋盘形式并加入 solutions
return;
}
// 尝试将皇后放置在当前行 row 的每一个列位置 col
for (int col = 0; col < n; col++) {
// 检查将皇后放置在 row 行 col 列是否合法
if (isValid(queens, row, col)) {
// 如果合法,将皇后放置在该位置
queens[row] = col;
// 递归尝试下一行的皇后放置
backtrack(solutions, queens, n, row + 1);
// 回溯:撤销当前行皇后的放置,尝试其他列
queens[row] = -1;
}
}
}
// 判断在 row 行 col 列放置皇后是否合法
private boolean isValid(int[] queens, int row, int col) {
for (int i = 0; i < row; i++) {
// 1. 检查列冲突:若当前皇后和之前某一行的皇后处于同一列,则冲突
if (queens[i] == col) {
return false;
}
// 2. 检查对角线冲突:通过 |row - i| 和 |col
代码详细解读
1. solveNQueens
方法
- 功能:这是解决 N 皇后问题的主方法,首先初始化一个大小为 N 的
queens
数组,用来存储每行皇后的位置。然后调用backtrack
方法开始回溯搜索。 - 变量说明:
solutions
:存储所有找到的解决方案,形式为棋盘的字符串表示。queens
:用于存储当前棋盘状态,queens[i]
表示第 i 行皇后所处的列位置,初始化为 -1 表示未放置。
- 递归开始:从第 0 行开始递归调用
backtrack
。
2. backtrack
方法
- 功能:递归地尝试为每一行放置皇后。通过检查是否能够在某列放置皇后来判断继续递归,直到找到合法解。
- 递归终止条件:当
row == n
时,表示所有行都成功放置皇后,此时将该解添加到solutions
中。 - 回溯思想:
- 在某一行的每一列中尝试放置皇后。
- 如果找到合法位置,递归进入下一行。
- 如果当前行没有合适位置,则回溯到上一行,继续尝试其他列。
3. isValid
方法
- 功能:用于判断在某一行的某一列放置皇后是否合法。
- 合法性检查:
- 同列冲突:检查当前列是否已经有其他皇后占据。
- 对角线冲突:通过检查两皇后行号和列号的差的绝对值是否相等,判断是否在同一对角线上。
4. generateBoard
方法
- 功能:将
queens
数组中的解转换成棋盘的字符串形式,便于输出和展示。 - 棋盘生成:对于每一行,根据
queens[i]
的值生成一行字符串,'Q'
表示皇后,'.'
表示空位。
5. 测试部分
- 功能:解决 4 皇后问题,并输出所有的可能解。可以通过修改
n
的值来解决不同大小的 N 皇后问题。
输出示例(4 皇后问题)
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
这个结果展示了 4 皇后问题的两种有效解。每行的 Q
表示皇后,.
表示空位。每种解中,皇后彼此之间没有相互攻击。
时间复杂度分析
由于 N 皇后问题的解空间是 N 的阶乘 (N!) 级别,每一行最多有 N 种选择,因此时间复杂度大约为 O(N!)。