题目
N 皇后问题是指在 n * n 的棋盘上要摆 n 个皇后。
要求:任何两个皇后不同行,不同列也不在同一条斜线上。
求给一个整数 n ,返回 n 皇后的摆法数。
数据范围: 1≤n≤9。
要求:空间复杂度 O(1) ,时间复杂度 O(n!)。
例如当输入4时,对应的返回值为2,
对应的两种四皇后摆位如下图所示:
示例1
输入:1
返回值:1
示例2
输入:8
返回值:92
思路:回溯
回溯可以理解为:通过选择不同的岔路口来通往目的地(找到想要的结果)
- 每一步都选择一条路出发,能进则进,不能进则退回上一步(回溯),换一条路再。
- 树、图的深度优先搜索(DFS)、八皇后、走迷宫都是典型的回溯应用。
回溯算法模板框架:
void backtracking(参数) { if (终止条件) { 存放结果; return; } for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); // 递归 回溯,撤销处理结果 } }
①三皇后:
②四皇后:
由图中不难看出 3、5 都发生了回溯,那么什么是剪枝思想呢?
如果第一次选的是0下标节点,那么和这个节点的斜对角节点1,和这个节点的同一列的节点0都不会再被选中,因为是按一行一行进行选择的所以行的限制不做考虑。这种越过一部分的节点不做选择的操作称为剪枝操作。
③八皇后:
由上面的四皇后我们可以推及到8皇后问题的操作:
和四皇后的方法一样一行一行进行选择,如果发现能放的节点数不够存储剩下皇后则回溯到上一个操作,重新选择节点。(上面的图并不是完整的回溯过程~)
④n皇后:
不同行不同列,那么肯定棋盘每行都会有一个皇后,每列都会有一个皇后。如果我们确定了第一个皇后的行号与列号,则相当于接下来在n−1行中查找n−1个皇后,这就是一个子问题,因此使用递归:
- 终止条件: 当最后一行都被选择了位置,说明n个皇后位置齐了,增加一种方案数返回。
- 返回值: 每一级要将选中的位置及方案数返回。
- 本级任务: 每一级其实就是在该行选择一列作为该行皇后的位置,遍历所有的列选择一个符合条件的位置加入数组,然后进入下一级。
具体做法:
- step 1:对于第一行,皇后可能出现在该行的任意一列,我们用一个数组chess记录皇后出现的位置。
- step 2:如果皇后出现在第一列,那么第一行的皇后位置就确定了,接下来递归地在剩余的n−1行中找n−1个皇后的位置。
- step 3:每个子问题检查是否符合条件,我们可以对比所有已经记录的行,对其记录的列号查看与当前行列号的关系:即是否同行、同列或是同一对角线。
代码
import java.util.*;
public class Solution {
/**
* @param n int整型 the n
* @return int整型
*/
public int Nqueen (int n) {
return solveNQueens(n).size();
}
public List<List<String>> solveNQueens(int n) {
char[][] chess = new char[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
chess[i][j] = '.';
}
}
List<List<String>> res = new ArrayList<>();
solve(res, chess, 0);
return res;
}
private void solve(List<List<String>> res, char[][] chess, int row) {
if (row == chess.length) {
res.add(construct(chess));
return;
}
for (int col = 0; col < chess.length; col++) {
if (valid(chess, row, col)) {
chess[row][col] = 'Q';
solve(res, chess, row + 1);
chess[row][col] = '.';
}
}
}
//row表示第几行,col表示第几列
private boolean valid(char[][] chess, int row, int col) {
//判断当前列有没有皇后,因为他是一行一行往下走的,我们只需要检查走过的行数即可,通俗一点就是判断当前坐标位置的上面有没有皇后
for (int i = 0; i < row; i++) {
if (chess[i][col] == 'Q') {
return false;
}
}
//判断当前坐标的右上角有没有皇后
for (int i = row - 1, j = col + 1; i >= 0 && j < chess.length; i--, j++) {
if (chess[i][j] == 'Q') {
return false;
}
}
//判断当前坐标的左上角有没有皇后
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chess[i][j] == 'Q') {
return false;
}
}
return true;
}
//把数组转为list
private List<String> construct(char[][] chess) {
List<String> path = new ArrayList<>();
for (int i = 0; i < chess.length; i++) {
path.add(new String(chess[i]));
}
return path;
}
}