形如
public static void test(int n) {if (n > 2) {test(n - 1);}System.out.println("n=" + n);
}
重要規則
- 執行一個方法時,就創建一個新的受保護的獨立空間(棧空間)
- 方法的局部變量是獨立的,不會相互影響
- 如果方法中使用的是引用類型變量,就會共享該引用類型的數據
- 遞歸必須向退出遞歸的條件逼近,否則就是無限遞歸,出現棧溢出,死龜了:)
- 當一個方法執行完畢,或者遇到return,就會返回,遵守誰調用,就將結果返回給誰,同時當方法執行完畢或者返回時,該方法也就執行完華。
主要解決問題
- 各種數學問題如:8皇后問題,漢諾塔,階乘問題,迷宮問題,球和籃子的問題(googl編程大賽)
- 各種算法中也會使用到遞歸,比如快排,歸并排序,二分查找,分治算法等
- 將用棧解決的問題->第歸代碼比較簡潔
迷宮回溯問題
/*** 如果小球能到map[6][5]位置,則說明通路找到* 約定:在map[i][j] 為 0 表示該點沒有走過 當為 1 表示墻; 2 表示通路可以走; 3表示該點已經走過,但是走不通* 在走迷宮時,需要確定一個策略(方法) 下->右->上->左,如果該點走不通,再回溯** @param map 表示地圖* @param i 表示從地圖的哪個位置開始出發(1,1)* @param j* @return*/
public static boolean setWay(int[][] map, int i, int j) {if (map[6][5] == 2) {// 通路已經找到return true;} else {if (map[i][j] == 0) {// 該點還沒有走過map[i][j] = 2; // 假定該點可以走通if (setWay(map, i + 1, j)) {// 向下走return true;} else if (setWay(map, i, j + 1)) {// 向右走return true;} else if (setWay(map, i - 1, j)) {// 向上走return true;} else if (setWay(map, i, j - 1)) {// 向左走return true;} else {// 說明該點是走不通,是死路map[i][j] = 3;return false;}} else { // 如果map[i][j] != 0,可能是1,2,3return false;}}
}
八皇后遞歸問題
八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾手
1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即:任意兩個皇后都不能處于同一行、同一列或同一斜線上,問有多少種擺法(92)。
算法思路
- 第一個皇后先放第一行第一列
- 第二個皇后放在第二行第一列、然后判斷是否0K,如果不0K,繼續放在第二列、第三列、依次把所有列都放完,找到一個合適
- 繼續第三個皇后,還是第一列、第二列.…直到第8個皇后也能放在一個不沖突的位置,算是找到了一個正確解
- 當得到一個正確解時,在棧回退到上一個棧時,就會開始回溯,即將第一個皇后,放到第一列的所有正確解,全部得到.
- 然后回頭繼續第一個皇后放第二列,后面繼續循環執行1,2,3,4 的步驟
說明:理論上應該創建一個二維數組來表示棋盤,但是實際上可以通過算法用一個一維數組即可解決問題 arr[8] = {0,4,7,5,2,6,1,3} 表示 對應arr 下標 表示第幾行,即第幾個皇后,arr[i] = val,val表示第 i+1 個皇后,放在第 i+1 行的第 val+1 列
代碼
package com.xiaolu.recursion;/*** @author 林小鹿* @version 1.0* 八皇后遞歸*/
public class Queue8 {// 定義一個max表示共有多少個皇后int max = 8;// 定義數組arry,保存皇后放置位置的結果,如 arr[] = {0,4,7,5,2,6,1,3}int[] array = new int[max];static int count = 0;static int judgeCount = 0;public static void main(String[] args) {Queue8 queue8 = new Queue8();queue8.check(0);System.out.printf("一共有%d種解法", count);System.out.printf("\n一共判斷%d次", judgeCount);}// 放置第 n 個皇后private void check(int n) {if (n == max) { // n = 8 時,第八個皇后就已經放好了print();return;}// 依次放入皇后,并判斷是否沖突// 注意:check 是每一次遞歸時,進入check中都有一個for循環,并且重復執行回溯的過程for (int i = 0; i < max; i++) {// 先把當前這個皇后 n 放到該行的第 1 列array[n] = i;// 判斷是否沖突if (judge(n)) {// 不沖突// 接著放 n+1 個皇后,開始遞歸check(n + 1);}// 如果沖突,就繼續執行 array[n] = i;即 將第 n 個皇后放置在本行的后一個位置}}/*** 判斷檢測皇后是否和前面已經擺放的皇后沖突** @param n 表示第 n 個皇后* @return*/private boolean judge(int n) {judgeCount++;for (int i = 0; i < n; i++) {// array[i] == array[n] 判斷是否通一列// Math.abs(n-i) == Math.abs(array[n] - array[i]) 判斷是否同一斜線// 沒有必要判斷是否在同一行,因為 n 每次都在遞增if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {return false;}}return true;}// 輸入皇后擺放的位置private void print() {count++;for (int i = 0; i < array.length; i++) {System.out.print(array[i] + " ");}System.out.println();}
}