回溯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。