看個實際應用場景, 迷宮問題(回溯), 遞迴(recursion)
簡單的說: 遞迴就是方法自己呼叫自己,每次呼叫時傳入不同的變數.遞迴有助於程式設計者解決複雜的問題,同時可以讓**變得簡潔。
列舉兩個小案例,回顧一下遞迴呼叫機制
列印問題
階乘問題
使用**方式說明了遞迴的呼叫機制
public class recursiontest
public static void test(int n)
// !!!當有 else 時,結果不同
//else }}
public static int factorial(int n) else
}
遞迴用於解決什麼樣的問題
各種數學問題如: 8 皇后問題 , 漢諾塔, 階乘問題, 迷宮問題, 球和籃子的問題(google 程式設計大賽)
各種演算法中也會使用到遞迴, 比如快排, 歸併排序, 二分查詢, 分治演算法等
將用棧解決的問題 --> 遞迴**比較簡潔
遞迴需要遵守的重要規則:
執行乙個方法時, 就建立乙個新的受保護的獨立空間(棧空間)
方法的區域性變數是獨立的, 不會相互影響, 比如 n 變數
如果方法中使用的是引用型別變數(比如陣列), 就會共享該引用型別的資料.
遞迴必須向退出遞迴的條件逼近, 否則就是無限遞迴,出現 stackoverflowerror
當乙個方法執行完畢, 或者遇到 return, 就會返回, 遵守誰呼叫, 就將結果返回給誰, 同時當方法執行完畢或者返回時, 該方法也就執行完畢
從 (1,1) 開始,小球走到右下角 (6,5)
public class mymigong
/*** 使用遞迴回溯來給小球找路
* 說明
* 1. map 表示地圖
* 2. i,j 表示從地圖的哪個位置開始出發 (1,1)
* 3. 如果小球能到 map[6][5] 位置,則說明通路找到.
* 4. 約定: 當map[i][j] 為 0 表示該點沒有走過 當為 1 表示牆 ; 2 表示通路可以走 ; 3 表示該點已經走過,但是走不通
* 5. 在走迷宮時,需要確定乙個策略(方法) 下->右->上->左 , 如果該點走不通,再回溯
** @param map
* @param i
* @param j
* @return 如果找到通路,就返回true, 否則返回false
*/private static boolean setway(int map, int i, int j) else else if (setway(map, i, j + 1)) else if (setway(map, i - 1, j)) else if (setway(map, i, j - 1)) else
} else }}
private static int initmap()
// 左右全部置為1
for (int i = 0; i < 8; i++)
//設定擋板, 1 表示
map[3][1] = 1;
map[3][2] = 1;
// map[1][2] = 1;
// map[2][2] = 1;
return map;
}private static void printmap(int map)
system.out.println();}}
}
再得到小球路徑時, 可以先使用(下右上左), 再改成(上右下左), 看看路徑是不是有變化
測試回溯現象
思考: 如何求出最短路徑? 思路 --> **實現.
八皇后問題, 是乙個古老而著名的問題, 是回溯演算法的典型案例。 該問題是國際西洋棋棋手馬克斯· 貝瑟爾於 1848 年提出: 在 8×8 格的西洋棋上擺放八個皇后, 使其不能互相攻擊, 即: 任意兩個皇后都不能處於同一行、同一列或同一斜線上, 問有多少種擺法 (92)。
第乙個皇后先放第一行第一列
第二個皇后放在第二行第一列、 然後判斷是否 ok, 如果不 ok, 繼續放在第二列、 第三列、 依次把所有列都放完, 找到乙個合適
繼續第三個皇后, 還是第一列、 第二列……直到第 8 個皇后也能放在乙個不衝突的位置, 算是找到了乙個正確解
當得到乙個正確解時, 在棧回退到上乙個棧時, 就會開始回溯, 即將第乙個皇后, 放到第一列的所有正確解,全部得到.
然後回頭繼續第乙個皇后放第二列, 後面繼續迴圈執行 1,2,3,4 的步驟
說明:理論上應該建立乙個二維陣列來表示棋盤, 但是實際上可以通過演算法, 用乙個一維陣列即可解決問題.
arr[8] =
對應 arr 下標 表示第幾行, 即第幾個皇后, arr[i] = val , val 表示第 i+1 個皇后, 放在第 i+1 行的第 val+1 列
public class myqueen8
int array = new int[max];
static int count = 0;
static int judgecount = 0;
static int countarr = new int[8];
static int x = 0;
public static void main(string args)
//編寫乙個方法,放置第n個皇后
//特別注意: check 是 每一次遞迴時,進入到check中都有 for(int i = 0; i < max; i++),因此會有回溯
private void check(int n)
//依次放入皇后,並判斷是否衝突
for (int i = 0; i < max; i++)
//如果衝突,就繼續執行 array[n] = i; 即將第n個皇后,放置在本行得 後移的乙個位置
if (n == 0) else
x++;}}
}private int calc(int arr, int n) else
}/**
* 檢視當我們放置第n個皇后, 就去檢測該皇后是否和前面已經擺放的皇后衝突
* * @param n 表示第n個皇后
* @return
*/private boolean judge(int n)
}return true;
}//寫乙個方法,可以將皇后擺放的位置輸出
private void print()
system.out.println();}}
第5章 遞迴
目錄 四 遞迴程式設計的應用例項 大綱未規定 五 演算法設計題 六 錯題集 資料結構與演算法 師大完整教程目錄 更有python go pytorch tensorflow 爬蟲 人工智慧教學等著你 遞迴 直接或間接的呼叫函式本身 遞迴程式的兩個特點 具備遞迴出口 在不滿足遞迴出口的情況下,把原問題...
第 6章 函式
6.1.2引數 2.引數陣列 c 允許為函式指定乙個 只能乙個 特殊的引數,這個引數必須是函式定義中的最後乙個引數,可用params關鍵字定義他們 如 params int vals 3.引用引數和值引數 理解 將本來在函式中引數按值引用的規則改變成按傳遞引用,使得這個引數會改變,定義引數和傳遞引數...
第6章 函式
1.自動物件 只存在於塊執行期間的物件 2.區域性靜態物件static 在程式執行路徑第一次經過物件定義語句時初始化,並且知道程式終止才被銷毀,如果區域性靜態變數沒有顯示的初始值,初始化為0.3.如果函式無須改變引用形參的值,最好將其生命為常量引用。4.使用引用形參返回額外資訊 5.和其他初始化過程...