文章目录
- 1 题目理解
- 2 回溯
- 2.1 直观解法
- 2.2 按行遍历
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的判断。
时间复杂度:有n2n^2n2个位置,每个位置有9种可能,所以最终时间复杂度n18n^{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种…