本来还有一道 332.重新安排行程 着实不太能看懂,后续跟上......
51. N皇后
先进行画图
借用卡哥的图,一个3 * 3 二维矩阵中放置Q:
由图可知:二维矩阵中的高便是树的高度,矩阵的宽便是树形结构中的每一个节点的宽度
棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就直接套进回溯法的模板里了。
所以,直接满足Q的约束条件进行搜索,搜索到叶子节点,便说明找到了合理位置:
完整代码如下:
class Solution {
List<List<String>> res = new LinkedList<>();
public List<List<String>> solveNQueens(int n) {
List<String> board = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; i++){
sb.append('.');
}
for(int i = 0; i < n; i++){
board.add(sb.toString());
}
backboard(0, board);
return res;
}
public void backboard(int row, List<String> board){
if(row == board.size()){
res.add(new ArrayList<>(board));
return;
}
int n = board.get(row).length();
for(int col = 0; col < n; col++){
// 剪枝
if(isValid(board, row , col) == false){
continue;
}
char[] arr = board.get(row).toCharArray();
arr[col] = 'Q';
// 存放路径
board.set(row, String.valueOf(arr));
backboard(row + 1, board);
// 撤销选择
arr[col] = '.';
board.set(row, String.valueOf(arr));
}
}
public boolean isValid(List<String> board, int row, int col){
int n = board.size();
// 列冲突 -> 检查该行的每一列
for(int i = 0; i <= row; i++){
if(board.get(i).charAt(col) == 'Q'){
return false;
}
}
// 检查右上
for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++){
if(board.get(i).charAt(j) == 'Q'){
return false;
}
}
// 检查左上
for(int i = row - 1, j = col - 1; i>= 0 && j >= 0; i--, j--){
if(board.get(i).charAt(j) == 'Q'){
return false;
}
}
return true;
}
}
37. 解数独
该题目与之前回溯算法不同的是“二维递归”
一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!
决策树如下:
首先写出数独中的约束条件:
- 同行是否重复
- 同列是否重复
- 9宫格里是否重复
public boolean isValid(char[][] board, int row, int col, char ch){
for(int i = 0; i < 9; i++){
// 行重复
if(board[row][i] == ch){
return false;
}
}
for(int i = 0; i < 9; i++){
// 列重复
if(board[i][col] == ch){
return false;
}
}
// 3 * 3方框重复
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] == ch){
return false;
}
}
}
return true;
}
重点:
因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值。
并且,本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。
写出递归函数代码如下:
public boolean backtrack(char[][] board){
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
// 遇见已经填过的数字
if(board[i][j] != '.'){
continue;
}
for(char ch = '1'; ch <= '9'; ch++){
// 不满足数独要求
if(isValid(board, i, j, ch) == false){
continue;
}
board[i][j] = ch;
// 当在该格子找到可行解时,便及时递归下一个格子
if(backtrack(board) == true){
return true;
}
board[i][j] = '.';
}
// 当尝试填 9 个数字都不符合时
return false;
}
}
// 当遍历完所有格子
return true;
}