回溯算法总结篇
332.重新安排行程
51.N皇后
回溯法:
- 横向遍历列, 纵向遍历行, 所以把行当作递归参数传入
- 对于当前遍历的位置(row,col)必须进行check判断该位置放置N皇后是否符合规则,
- 若符合规则, 则继续进行递归,进入到下一行放置皇后
- 若不符合规则, 则continue
- 若底层递归失败, 返回到上一层, 则代表上层的该位置不能放置皇后, 所以需要进行回溯
代码:
class Solution {
// 回溯法: 行看成树的每一层, 列看成纵向
List<List<String>> ans = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
// 位置标记数组
char[][] used = new char[n][n];
for(int i=0; i<used.length; i++){
Arrays.fill(used[i], '.');
}
backtracking(n, used, 0);
return ans;
}
public void backtracking(int n, char[][] used, int row) {
if(row == n) {
LinkedList<String> list = new LinkedList<>();
for(int i=0; i < n; i++){
String path = "";
for(int j=0; j < n; j++) {
path += used[i][j];
}
list.add(path);
}
ans.add(new ArrayList<>(list));
return;
}
for(int col=0; col < n; col++) {
if(check(used, row, col, n)){
used[row][col] = 'Q';
backtracking(n, used, row + 1);
used[row][col] = '.';
}
else if(col == n-1){
return;
}else{
continue;
}
}
}
public boolean check(char[][] used, int row, int col, int n){
// 检查同一列
for(int i=0; i<row; i++){
if(used[i][col] == 'Q') return false;
}
// 检查左上角对角线
for (int i=row-1, j=col-1; i>=0 && j>=0; i--, j--) {
if (used[i][j] == 'Q') return false;
}
// 检查右上角对角线
for (int i=row-1, j=col+1; i>=0 && j<=n-1; i--, j++) {
if (used[i][j] == 'Q') return false;
}
return true;
}
}
37.解数独
回溯法:
- 本题和
N皇后问题不同,N皇后问题是每行只放置一个元素, 但是本题是每个位置都必须得放置一个值 - 所以本题需要双层
for循环来遍历每一个位置 - 同样的对于每一个位置都必须
check, 若该位置不为'.', 则代表有值, 不能再放置元素 - 同时
check同行同列和九宫格是否重复 - 本题的递归出口在于: 只要双层
for循环执行完毕, 那么就代表所有位置都找到对应的元素, 所以return true, 在递归中, 只要接收到下层的true, 就继续向上return true, 直到结束所有递归 - 若遍历完
1-9还未找到合适元素, 则代表之前的位置有问题, 需要向上return false, 向上回溯
代码:
class Solution {
public void solveSudoku(char[][] board) {
backtracking(board);
return;
}
// 行用纵向遍历代替, 列用横向遍历代替
// 回溯有没有返回值, 区别在于是找到一种结果还是找到所有可能方案
public boolean backtracking(char[][] board){
// 每行每列都得填满
for(int row=0; row < 9; row++){
for(int col=0; col < 9; col++){
if(board[row][col] == '.'){
for(char curr='1'; curr <= '9'; curr++){
if(check(board, row, col, curr)){
board[row][col] = curr;
if(backtracking(board)) return true;
board[row][col] = '.';
}
}
// 若走到当前位置, 则代表(row, col)位置填数字1-9都不行, 只能向上返回false, 进行回溯
return false;
}
}
}
return true;
}
// check
public boolean check(char[][] board, int row, int col, int curr){
// check the row
for(int j=0; j<9; j++){
if(board[row][j] == curr) return false;
}
// check the col
for(int i=0; i<9; i++){
if(board[i][col] == curr) return false;
}
// 检查九宫格
int m = row / 3 * 3;
int n = col / 3 * 3;
for(int i=m; i < m+3; i++){
for(int j=n; j < n+3; j++){
if(board[i][j] == curr)
return false;
}
}
return true;
}
}