概念
回溯法是一种优选的搜索法,又称试探法。按选优条件向前搜索,已达到目标。但当搜索到某一步时,发现原选择并不优或者达不到目标,就退一步重新选择。这种走不通就退回再走的技术称为回溯法。
特点
1、出口
它的关键是出口语句放置的位置(建议出口语句放在递归函数的第一行),出口语句写在最前面,方便整个函数退出。
2、递归函数的参数
参数是随每一次的递归操作二发生改变的,二回溯的关键就是:如果当前操作行不通,如何回溯到上一部操作。解决办法就是不破坏当前值,即在当前值下一次递归调用时,把当前值按规律进行变换,变换的过程作为参数处低下去。
3、递归函数处理过程
如果当前递归过程额处理参数符合要求,则执行相关赋值或其他操作,然后转入下一次递归,如果下一次递归不能找到出口,则把之前相关赋值或其他操作重置为初始状态。
个人理解
1、构造解空间,即递归范围,在当前解空间中,便于搜索到答案
2、搜索策略:深度优先搜索
3、剪枝:去掉大量不必要的搜索以提高效率
关键特征
带优化的暴力穷举
总结
回溯法用到的核心思想就是递归,虽然过程逻辑清楚,而且执行效率很高,但缺点是逻辑清楚,必然代表抽象性很高。所以有时或是这样的情况,它的解题过程很容易看懂,但是要让自己动手写这个递归过程,发现很难下笔。
举例“八皇后”问题
该问题是国际西洋棋起手马克思·贝瑟尔于1848年提出,在8*8的国际象棋上,摆放8个皇后,使其不能互相共计,即任意两个皇后不能处在同一行、同一列、同一斜线上。问有多少种摆法。
代码实现
public class Main {
private static int count = 0; //记录成功的第几种可能
private static int N = 8; //几皇后
public static void main(String[] args) {
int[][] arr = new int[N][N]; //默认元素为0 1当皇后
eightQueen(0, arr); //打印八皇后所有可能的解 并且从第一行开始 0
}
//row [0,7]
private static void eightQueen(int row, int[][] arr) {
if (row == N) { //row可以到达8 也就意味着前0~7行 每行都放有皇后
count++;
System.out.println("第" + count + "种:");
for (int i = 0; i < arr.length; i++) { //循环输出二维数组
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
} else { //如果没有完成八皇后 复制一个副本 对其副本进行改变
//做一个数组备份
int[][] newArr = new int[N][N];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
newArr[i][j] = arr[i][j];
}
}
for (int col = 0; col < N; col++) { //遍历 某行的每一列的元素
if (noDangerous(row, col, newArr)) { //判断这个位置是否可以放皇后 如果可以放
for (int c = 0; c < N; c++) { //先清空这一行的元素(有可能以前这行有皇后 如果不清理则这行会有多个皇后)
newArr[row][c] = 0;
}
newArr[row][col] = 1; //有皇后用1代表 没皇后用0代表
eightQueen(row + 1, newArr);//当前行找到皇后的位置后 在这个情况下 找下一行皇后放置的位置,这一个递归调用结束之后 在返回上一行 寻找其他的可能 在向重复上述的操作
}
}
}
}
private static boolean noDangerous(int row, int col, int[][] newArr) { //判断这个位置是否可以放皇后
//正上
for (int r = row - 1; r >= 0; r--) {
if (newArr[r][col] == 1) {
return false;
}
}
//左上
for (int r = row - 1, c = col - 1; r >= 0 && c >= 0; r--, c--) {
if (newArr[r][c] == 1) {
return false;
}
}
//右上
for (int r = row - 1, c = col + 1; r >= 0 && c < N; r--, c++) {
if (newArr[r][c] == 1) {
return false;
}
}
return true;
}
}