你們好,我是阿濠,今篇內容跟你們分享的是算法之遞歸,很高興分享到segmentfault與你們一塊兒學習交流,初次見面請你們多多關照,一塊兒學習進步.
簡單的來講,遞歸就是本身調用本身
,每次調用時傳入不一樣的變量
,遞歸有助於編程者解決複雜的問題
,同時讓本身代碼變成簡潔算法
列舉兩個小案例,認識遞歸調用機制編程
1.打印問題、2.階乘問題segmentfault
//1.打印問題 public class RecursionTest { public static void main(String[] args) { //經過打印問題,回顧遞歸調用機制 test(4); } public static void test(int n) { if (n > 2) { test(n - 1); } System.out.println("n=" + n); } }
遞歸在咱們的程序內部
是如何調用
的?程序從main
方法入口進入
數組
當咱們進入main
方法調用test(4)
時,開闢獨立空間學習
執行
到test(4)
方法時,會有變量n = 4
並進行if判斷(4>2)
,執行test(4-1)
方法測試
此時執行test(3)
方法時,n = 3
也進行if判斷(3>2)
,並執行test(3-1)
方法優化
此時執行test(2)
方法時,n = 2
也進行if判斷(2>2)
,可是2>2 不成立
,執行輸出指令網站
此時test(2)
執行完畢後控制檯進行輸出的結果是 n =2
當test(2)執行完後就退到test(3).. n=三、test(4)... n=4
ui
public class RecursionTest { public static void main(String[] args) { int res=factorial(3); System.out.println("結果="+res); } //階乘問題 public static int factorial(int n) { if(n==1){ return 1; } else { return factorial(n - 1) * n; } } }
按照剛剛示意圖的理解,就先是factorial(3 - 1 ) * 3
、factorial(2 - 1 ) * 2
、而factorial(1)直接return 1
,因此輸出結果就是1 * 2 * 3=6
spa
1.當程序執行
到一個方法時
,就會開闢獨立的空間(棧)
2.每一個空間的數據
(局部變量),是獨立的
1.各類數學問題:
如:8皇后問題,漢諾塔,階乘問題,迷宮問題,球和籃子的問題等等
2.各類算法中也會使用到遞歸
好比快排,歸併排序,二分查找,分治算法等等.
3.將用棧解決的問題-->第歸代碼比較簡潔
1)執行
一個方法
時,就建立
一個新的受保護的獨立空間(棧空間)
2)方法的局部變量是獨立的
,不會相互影響
,好比以前舉例的 n 變量
3)若是方法中是使用引用變量
,即會共享引用類型的數據
,即迷宮問題時方法傳遞是數組,數組是引用類型那麼就會進行調用同一個數據:數組
4)遞歸必須向退出遞歸的條件逼近
,不然就是無限遞歸
,死龜了拋出異常:StackOverflowError
5)當一個方法執行完畢
,或者遇到return, 就會返回
,遵照誰調用,就將結果返回給誰
,同時當方法執行完畢或者返回時,該方法也就執行完畢。
1.建立二維數組模擬迷宮
2.使用1表明牆,上下全是1,便是列在變,行沒變,因此範圍是0-6在變
3.使用1表明牆,左右全是1,便是行在變,列沒變,因此範圍是0-7在變
4.使用1表明擋板,即小球沒法走到擋板的位置,按圖所示第四行的一、2列
5.約定:0 表明沒有走過、1 表示牆、2表示路能夠走、3已走過但不通
6.按圖所示,起點爲止從(1,1)開始(第二行第二列),小球在(6,5)的位置
7.若小球爲(i,j),向左走是(i,j-1),向右走是(i,j+1),向上走是(i-1,j),向下是(i+1,j)
//先建立一個二維數組,模擬迷宮地圖 int[][] map = new int[8][7]; //使用1表示牆 //上下所有置爲1 for (int i = 0; i < 7; i++) { map[0][i] = 1; map[7][i] = 1; } //左右所有置爲1 for (int i = 0; i < 8; i++) { map[i][0] = 1; map[i][6] = 1; } //設置擋板, 1表示 map[3][1] = 1; map[3][2] = 1; //輸出地圖 System.out.println("地圖的狀況"); for (int i = 0; i < 8; i++) { for (int j = 0; j < 7; j++) { System.out.print(map[i][j] + " "); } System.out.println(); } 運行結果: 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1
//使用遞歸回溯來給小球找路 //說明 //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 */ public static boolean setWay(int[][] map, int i, int j) { //小球的位置是(6,5) 對應數組則是map[6][5] if(map[6][5] == 2) { //到map[6][5]位置,說明球已找到 return true; } else { //判斷到地圖map[i][j]是0,則表明這個點尚未走過 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, 1 - 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, 3 //當爲1表示牆; 2表示通路能夠走; 3表示該點已經走過 return false; } } }
步驟分析圖:
.....
八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯●貝瑟爾於1848年提出:在8X8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即:任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法(92)
。
1.第一個皇后
先放第一行第一列
2.第二個皇后
放在第二行第一列
,而後判斷是否0K
,若是不OK
, 繼續放在第二列、第三列
...依次把全部列都放完,找到合適
的位置
3.繼續第三個皇后
仍是第一列、第二列...
`直到第八個皇后都放在一個
不衝突的位置,算是找到了
一個正確解`
4)當獲得一個正確解
時,棧回退到上一個棧
,開始回溯
,即第一個皇后
,放到第一列
的全部正確解
,所有獲得
.
5)而後回頭繼續把第一個皇后
放第二列
,後面繼續循環執行1,2,3的步驟
按照思路先將.第一個皇后
先放第一行第一列
(小圓圈表明皇后)
第二個皇后
放在第二行第一列
,而後判斷是否0K
,顯然條件是:兩個皇后都不能處於同一行、同一列或同一斜線上
因而第二個皇后
再向右移動一個位置,知足條件不是相互攻擊
因而繼續第三個皇后
仍是第一列
開始,顯然條件是:兩個皇后都不能處於同一行、同一列或同一斜線上...第...皇后
這時獲得一個正確的解就會進行把第一個皇后
放第二列
,後面繼續循環執行1,2,3的步驟
,可是效率並不高,由於8x8的會執行一萬屢次,後面會有算法進行優化
理論上應該建立一個二維數組來表示棋盤,可是實際上能夠經過算法用一個一維數組便可解決問題:arr[8]= {0,4, 7,5,2, 6, 1, 3}
即arr下標表示第幾行,即第幾個皇后,arr[i] =val, val表示第i+1個皇后,放在第i+1行的第i+1列
即咱們使用下標表示第幾行是第幾個皇后
,按圖所示那麼對應的值如果一致則表明在同一列:array[n]==array[i]
即咱們使用下標表示第幾行是第幾個皇后
,那麼第n個皇后與第n-1皇后的列差 == 第n個皇后與第n-1行差
,那麼則是同一斜線上:Math.abs(n - i) == Math.abs(array[n] - array[i])
,好比說第二個皇后與第一個皇后的行差=一、列差=1,相等證實按圖所示它們是斜線
實際操做代碼以下:
public class Queue8 { //定義一個max表示共有多少個皇后 int max = 8; //定義數組array,保存皇后放置位置的結果,好比arr = {0 , 4, 7, 5, 2, 6, 1, 3} int[] array = new int[max]; //放置第n個皇后 public void check(int n){ //從0開始放置 若n=8 則表明最後一列的皇后了 if( n == max ){ printQueue(); return; } //依次放入皇后 for(int i=0; i<max; i++){ //把當前皇后n,放入第一列位置 array[n]=i; //放入第n個皇后檢查是否衝突 if(judge(n)){ //若不衝突,則執行n+1個皇后 check(n+1); } //若衝突 則繼續執行arr[n] = i;即將第n個皇后 放置在本行的後移一個位置 } } //查看當咱們放置第n個皇后,就去檢測該皇后是否和前面已經擺放的皇后衝突 /** * @param n 表示第n個皇后 * @return */ private boolean judge(int n) { for(int i=0; i<n; i++) { //1. array[i] == array[n] 表示判斷 第n個皇后是否和前面的n-1個皇后在同一列 //2. Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判斷第n個皇后是否和第i皇后是否在同一斜線 //行差=列差 證實是斜線 if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) { return false; } } return true; } //寫一個方法,能夠將皇后擺放的位置輸出 private void printQueue(){ for (int i = 0; i < array.length; i++) { System. out. print(array[i] +""); } System.out.println(); } }
按照思路,添加數據測試一下把
public static void main(String[] args) { //測試八皇后 Queue8 queue8 = new Queue8(); queue8.check(0); } 運行結果以下: 04752613 05726314 06357142 06471352 13572064 14602753 14630752 15063724 15720364 16257403 16470352 17502463 20647135 24170635 24175360 24603175 24730615 25147063 25160374 25164073 25307461 25317460 25703641 25704613 25713064 26174035 26175304 27360514 30471625 30475261 31475026 31625704 31625740 31640752 31746025 31750246 35041726 35716024 35720641 36074152 36271405 36415027 36420571 37025164 37046152 37420615 40357162 40731625 40752613 41357206 41362750 41506372 41703625 42057136 42061753 42736051 46027531 46031752 46137025 46152037 46152073 46302751 47302516 47306152 50417263 51602473 51603742 52064713 52073164 52074136 52460317 52470316 52613704 52617403 52630714 53047162 53174602 53602417 53607142 57130642 60275314 61307425 61520374 62057413 62714053 63147025 63175024 64205713 71306425 71420635 72051463 73025164
總共是92種思路解法,發現就是按照思路圖的思路進行遞歸判斷,可進入小遊戲網站實踐一下:http://www.4399.com/flash/426...