回溯VS递归
很多人认为回溯和递归是一样的,其实不然。在回溯法中可以看到有递归的身影,但是两者是有区别的。
回溯法从问题本身出发,在包含问题的所有可能解的解空间树中,从根结点出发,按照深度优先的策略进行搜索,对于解空间树的某个结点,若满足约束条件,则进入该子树继续搜索,否则将以该结点为根结点的子树进行剪枝,逐层向其祖先结点回溯,寻找可能实现的所有情况。和穷举法的思想相近,不同在于穷举法是将所有的情况都列举出来以后再一一筛选,而回溯法在列举过程如果发现当前情况根本不可能存在,就停止后续的所有工作,返回上一步进行新的尝试。
递归从问题的结果出发,例如求 n!,要想知道 n!的结果,就需要知道 n*(n-1)! 的结果,而要想知道 (n-1)! 结果,就需要提前知道 (n-1)*(n-2)!。这样不断地向自己提问,不断地调用自己的思想就是递归。
回溯和递归唯一的联系就是,回溯法可以用递归思想实现。
N皇后问题
在n x n的棋盘上摆放n个皇后,而且n个皇后中的任意两个是不能处于同一行、同一列、或同一斜线上。
【分析】因为n皇后不能在同行,同列, 同斜线。
- 每一行放一个皇后,就解决了不在同行的问题。
- 在第i行的时候,遍历n列,试探位置。和之前所有行放的位置进行比较。
- 比较列:当前列 col 不等于之前所有列。 即col != arr[i] 。
- 比较斜线, 因为不再同一斜率为1或者-1的斜线。(row - i) / (col - arr[i]) != 1 或 -1
- 可以取巧用绝对值函数: abs(row-i) != abs(col-arr[i]) 。
public class NQueen {
private int n; // n个皇后
private long sum; // 可行解的数量
private int[] arr; // 当前的解
public NQueen(int n){
this.n = n;
sum = 0;
arr = new int[n+1];
}
// row col i arr[i]
public boolean Check(int row, int col){
// 每一行放一个,一定不在同一行
for(int i = 1; i < row; i++){
// 当前列是否与第一,二...列相同
// 当前点是否与第一,二...列斜率为正负1
if(col == arr[i] || Math.abs(row - i) == Math.abs(col - arr[i]))
return false;
}
return true;
}
public void FindNQueen(int k) {
if (k > n) { //遍历到叶子节点,找出一种解, sum+1
sum++;
return;
}
for (int i = 1; i <= n; i++) {
if (Check(k, i)) { //检查是否满足条件
arr[k] = i; //记录
FindNQueen(k + 1); //递归查找
}
}
}
public static void main(String args[]){
System.out.println("请输入n的值:");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
NQueen nQueen = new NQueen(n);
nQueen.FindNQueen(1);
System.out.println(nQueen.sum);
}
}
当N=8时,输出结果为92。