1 题目理解
The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space, respectively.
n皇后问题
输入:整数n
输出:不同的解决方法
规则:n个皇后不能在同一行、同一列、同一对角线上,反对角线也不可以。
2 回溯
2.1 直观解法
这道题目最直接的解法是,遍历每个位置,在这个位置判断是否可以放皇后,如果能放,就有两种选择:放、不放。如果不能放,则向前递归。这个思路和数独题目类似。不同之处是还需要考虑对角线和反对角线。(以下内容来自力扣)
对于对角线上的元素特点是横坐标-纵坐标的值相同。
例如对角线(0,0) (1,1),(2,2),横坐标-纵坐标=0
对角线(0,4)(1,5)(2,6),横坐标-纵坐标=-4
用数组int[] diagnol 表示对角线是否冲突。可以用差的绝对值,也可以i-j+n来将横坐标-纵坐标放在一个合理的数组下标范围内。
反对角线的特征是 横坐标+纵坐标 值固定。
例如反对角线(0,2)(1,1)(2,0),横坐标+纵坐标=2
反对角线(0,5)(1,4)(2,3),横坐标+纵坐标=5
用数组int[] antiDiagnol 表示反对角线是否冲突。
class Solution {
private List<List<String>> answer;
private int n;
private int[][] board;//表示第i行第j列是不是有皇后
private int[] rows;
private int[] cols;
private int[] diagnol;
private int[] antiDiagnol;
public List<List<String>> solveNQueens(int n) {
answer = new ArrayList<List<String>>();
this.n = n;
board = new int[n][n];
rows = new int[n];
cols = new int[n];
diagnol = new int[2*n];
antiDiagnol = new int[2*n];
dfs(0,0,0);
return answer;
}
private void dfs(int row,int col,int queueCount){
if(col == n){
if(queueCount==n){
List<String> list = new ArrayList<String>();
for(int i=0;i<n;i++){
String val = "";
for(int j=0;j<n;j++){
val+=(board[i][j]==1?"Q":".");
}
list.add(val);
}
answer.add(list);
}
}else{
int nextRow = (row+1)%n;
int nextCol = (nextRow==0?col+1:col);
if(canPutQueue(row,col)){
put(row,col);
dfs(nextRow,nextCol,queueCount+1);
clear(row,col);
}
dfs(nextRow,nextCol,queueCount);
}
}
private void put(int row,int col){
board[row][col]=1;
rows[row]=1;
cols[col] = 1;
diagnol[row-col+n]=1;
antiDiagnol[row+col]=1;
}
private void clear(int row,int col){
board[row][col]=0;
rows[row]=0;
cols[col] = 0;
diagnol[row-col+n]=0;
antiDiagnol[row+col]=0;
}
private boolean canPutQueue(int i,int j){
return rows[i]==0 && cols[j]==0 && diagnol[i-j+n]==0 && antiDiagnol[i+j]==0;
}
}
因为过程中有放和不放的操作,所以需要记录最终放了多少个皇后。所以有queueCount==n的判断。
时间复杂度:有
n
2
n^2
n2个位置,每个位置有9种可能,所以最终时间复杂度
n
18
n^{18}
n18
2.2 按行遍历
因为每个皇后最后一行上只能有一个,所以可以先按行遍历,在这一行一定要放一个。在处理每一行的时候可以枚举放在[0,n-1]的某一列。
class Solution {
private List<List<String>> answer;
private int n;
private int[][] board;//表示第i行第j列是不是有皇后
private int[] cols;
private int[] diagnol;
private int[] antiDiagnol;
public List<List<String>> solveNQueens(int n) {
answer = new ArrayList<List<String>>();
this.n = n;
board = new int[n][n];
cols = new int[n];
diagnol = new int[2*n];
antiDiagnol = new int[2*n];
dfs(0);
return answer;
}
private void dfs(int row){
if(row == n){
List<String> list = new ArrayList<String>();
for(int i=0;i<n;i++){
String val = "";
for(int j=0;j<n;j++){
val+=(board[i][j]==1?"Q":".");
}
list.add(val);
}
answer.add(list);
}else{
for(int col=0;col<n;col++){
if(canPutQueue(row,col)){
put(row,col);
dfs(row+1);
clear(row,col);
}
}
}
}
private void put(int row,int col){
board[row][col]=1;
cols[col] = 1;
diagnol[row-col+n]=1;
antiDiagnol[row+col]=1;
}
private void clear(int row,int col){
board[row][col]=0;
cols[col] = 0;
diagnol[row-col+n]=0;
antiDiagnol[row+col]=0;
}
private boolean canPutQueue(int i,int j){
return cols[j]==0 && diagnol[i-j+n]==0 && antiDiagnol[i+j]==0;
}
}
时间复杂度: O ( n ! ) O(n!) O(n!)第一行有n种放法,第二行有n-1种放法,第三行有n-2种…