回溯法解题套路
简单理解回溯就是暴力循环求解,但是此暴力求解并不是简单的for循环,而是对决策树循环遍历,回溯有一定的框架,根据学习内容,写下框架以及找的例子以便自己往后复习。算法套路文章皆以labuladong的书为主,结合自身理解,以java语言复现。
框架内容
遇到问题,首先思考这三个问题:
1.路径已经做出的选择;
2.选择列表:当前可以做出的选择;
3.结束条件,达到底层后,无法再做出选择的条件。
其框架可以写为:
List result = new ArrayList();
void backTrace(路径,选择列表):
if (满足条件) result 添加路径
for( 路径:选择列表){
判断条件
做出选择
backTrace(路径,选择列表)
撤回选择
两个实例
数组全排列问题
该题为LeetCode46题,整体思路与框架相似
class Solution {
List<List<Integer>> res;
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
res = new ArrayList<>();
if (len == 0) {
return res;
}
List<Integer> path = new ArrayList();
backTrace(nums, path);
return res;
}
private void backTrace(int[] nums, List<Integer> path) {
int n = nums.length;
if (path.size()== n) {
//添加的为List 需要新建ArrayList
res.add(new ArrayList(path));
return;
}
for (int i = 0;i<n;i++){
// 判断条件:去除重复,若path中存在数字,则跳过
if (path.contains(nums[i])) continue;
//添加路径
path.add(nums[i]);
backTrace(nums,path);
//删除路径
path.remove(path.size()-1);
}
}
}
经典N皇后
N皇后为LeetCode51题,整体思路与框架相似
注意点 java的Arraylist 以及字符串无法确定对象元素内部位置以及替换,需要将其转为数组,然后再转为所需要的内容
class Solution {
List<List<String>> res;
public List<List<String>> solveNQueens(int n ){
res = new ArrayList() ;
char[][] board = new char[n][n];
for (int i = 0;i<n;i++)
for(int j = 0;j<n;j++)board[i][j] = '.';
traceBack(board,0);
return res;
}
public void traceBack(char[][] board,int row){
//终止条件 ,
if (row == board.length){
res.add(construct(board));
return ;
}
for (int col =0 ;col<board.length;col++){
//判断条件 看皇后位置
if (!isValid( board, row,col)) continue;
//添加路径
board[row][col] ='Q';
//进入下一步
traceBack(board,row+1);
//删除路径
board[row][col] ='.';
}
}
//这个方法就是实现 字符串内部位置替换
public List<String> construct(char[][] board){
List<String> a = new ArrayList<>();
for (int i = 0;i<board.length;i++){
a.add(new String(board[i]));
}
return a;
}
//row表示第几行,col表示第几列
private boolean isValid(char[][] chess, int row, int col) {
//判断当前列有没有皇后,因为他是一行一行往下走的,
//我们只需要检查走过的行数即可,通俗一点就是判断当前
//坐标位置的上面有没有皇后
for (int i = 0; i < row; i++) {
if (chess[i][col] == 'Q') {
return false;
}
}
//判断当前坐标的右上角有没有皇后
for (int i = row - 1, j = col + 1; i >= 0 && j < chess.length; i--, j++) {
if (chess[i][j] == 'Q') {
return false;
}
}
//判断当前坐标的左上角有没有皇后
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chess[i][j] == 'Q') {
return false;
}
}
return true;
}
}
总结
遇到回溯, 默写框架,注意边界条件 灵活根据语言特性变换存储结构