12.18 51N皇后
题目:
思路:
(1)用回溯的思想寻找每一行皇后的位置
(2)皇后位置的约束,创建一个新函数。使得与之前行的皇后不在同一列或斜线
(3)45度斜线行列和相等;135度斜线行列差相等。
时间复杂度(n!)
class Solution {List<List<String>> res=new ArrayList<>();LinkedList<String> path=new LinkedList<>();int n;public List<List<String>> solveNQueens(int n) {this.n=n;traceback(0);return res;}public void traceback(int start){if(start==n){res.add(new LinkedList<>(path));return;}for(int i=0;i<n;i++){if(!isvalid(start,i)){continue;}//满足条件创建字符串加入pathStringBuilder sb=new StringBuilder();for(int j=0;j<n;j++){if(j==i){sb.append('Q');}else{sb.append('.');}}path.add(sb.toString());traceback(start+1);path.removeLast();}}public boolean isvalid(int row,int col){if(row==0)return true;for(int i=0;i<path.size();i++){String s=path.get(i);//同一列if(s.charAt(col)=='Q')return false;//同一斜线if(col+(row-i)>=0&&col+(row-i)<s.length() &&s.charAt(col+(row-i))=='Q')return false;if(col-(row-i)>=0&&col-(row-i)<s.length() &&s.charAt(col-(row-i))=='Q')return false;}return true;}
}// 方法2:使用boolean数组表示已经占用的直(斜)线
class Solution {List<List<String>> res = new ArrayList<>();boolean[] usedCol, usedDiag45, usedDiag135; // boolean数组中的每个元素代表一条直(斜)线public List<List<String>> solveNQueens(int n) {usedCol = new boolean[n]; // 列方向的直线条数为 nusedDiag45 = new boolean[2 * n - 1]; // 45°方向的斜线条数为 2 * n - 1usedDiag135 = new boolean[2 * n - 1]; // 135°方向的斜线条数为 2 * n - 1//用于收集结果, 元素的index表示棋盘的row,元素的value代表棋盘的columnint[] board = new int[n];backTracking(board, n, 0);return res;}private void backTracking(int[] board, int n, int row) {if (row == n) {//收集结果List<String> temp = new ArrayList<>();for (int i : board) {char[] str = new char[n];Arrays.fill(str, '.');str[i] = 'Q';temp.add(new String(str));}res.add(temp);return;}for (int col = 0; col < n; col++) {if (usedCol[col] | usedDiag45[row + col] | usedDiag135[row - col + n - 1]) {continue;}board[row] = col;// 标记该列出现过usedCol[col] = true;// 同一45°斜线上元素的row + col为定值, 且各不相同usedDiag45[row + col] = true;// 同一135°斜线上元素row - col为定值, 且各不相同// row - col 值有正有负, 加 n - 1 是为了对齐零点usedDiag135[row - col + n - 1] = true;// 递归backTracking(board, n, row + 1);usedCol[col] = false;usedDiag45[row + col] = false;usedDiag135[row - col + n - 1] = false;}}
}
12.18 37解数独
思路:
(1)如何回溯。卡哥说是二维递归,比之前做的所有回溯题都要多一维。要在每一行每一列选择1到9中的一个数,而例如n皇后只用在每一行选择一个位置。
【代码上好像是多了两维,是从一层for循环变成了三层。但实际上这种维度的变化相当于只是多了两个参数而已。而二维与一维的最大不同点就是不能用递归的一个参数来表示行列变化了,这样每次都要递归都要走一遍已经确定数字的格子,不过递归过的格子数有标记,也就还是从下一个格子开始了。】
(2)判断有效性。根据空格的行列和填入的数判断是否有效。
(3)难点:回溯函数中什么时候返回false,什么时候返回true。
class Solution {public void solveSudoku(char[][] board) {traceback(board);}public boolean traceback(char[][] board){for(int i=0;i<9;i++){for(int j=0;j<9;j++){if(board[i][j]!='.')continue;//!注意k是char,数字带引号for(char k='1';k<='9';k++){if(isvaild(board,k,i,j)){board[i][j]=k;if(traceback(board))return true;board[i][j]='.';}}return false;//说明当前方案下,ij空格填入任何数都无法满足方案}}return true;}public boolean isvaild(char[][] board,char k,int row,int col){//同行for(int j=0;j<9;j++){if(board[row][j]==k)return false;}//同列for(int i=0;i<9;i++){if(board[i][col]==k)return false;}//同九宫格int startRow=row/3*3;int startCol=col/3*3;for(int i=startRow;i<startRow+3;i++){for(int j=startCol;j<startCol+3;j++){if(board[i][j]==k)return false;}}return true;}
}