c#圖解教程_第十三章& 第十四章_委託和事件
簡介
程式呼叫自身的程式設計技巧稱為遞迴( recursion)。遞迴做為一種演算法在程式設計語言中廣泛應用。 一個過程或函式在其定義或說明中有直接或間接呼叫自身的一種方法,它通常把一個大型複雜的問題層層轉化為一個與原問題相似的規模較小的問題來求解,遞迴策略只需少量的程式就可描述出解題過程所需要的多次重複計算,大大地減少了程式的程式碼量。遞迴的能力在於用有限的語句來定義物件的無限集合。一般來說,遞迴需要有邊界條件、遞迴前進段和遞迴返回段。當邊界條件不滿足時,遞迴前進;當邊界條件滿足時,遞迴返回,也就是遞迴必須向退出遞迴的條件逼近,否則就是死迴圈,造成StackOverflowError(堆疊溢位錯誤)。遞迴,就是在執行的過程中呼叫自己。
缺點
遞迴演算法解題相對常用的演算法如普通迴圈等,執行效率較低。因此,應該儘量避免使用遞迴,除非沒有更好的演算法或者某種特定情況,遞迴更為適合的時候。在遞迴呼叫的過程當中系統為每一層的返回點、區域性量等開闢了棧來儲存。遞迴次數過多容易造成棧溢位等
案例
階乘
public static int factorial(int num) { if (num == 1) { return 1; } else { return factorial(num - 1) * num; } }
public static void main(String[] args) {int factorial = factorial(3); System.out.println("factorial = " + factorial); }
當進入到方法factorial時,會開闢一個棧,每次執行到 return factorial(num - 1) * num;都會重新開闢一個棧,當一個方法執行完畢或者遇到return就會將結果返回給呼叫方,該方法也就執行完畢,結果就是6
迷宮問題
/** * @param map 地圖 * @param i 起始位置 * @param j 起始位置 * @return 是否找到路 * * 0是沒有走過,1是牆,2是可以走,3是已經走過帶上不通*/ 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 { return false; } } }
int[][] map = new int[8][7]; //1代表牆 for (int i = 0; i < 8; i++) { map[i][0] = 1; map[i][6] = 1; } for (int i = 0; i < 7; i++) { map[0][i] = 1; map[7][i] = 1; } map[3][1] = 1; map[3][2] = 1; for (int i = 0; i < 8; i++) { for (int j = 0; j < 7; j++) { System.out.print(map[i][j] + " "); } System.out.println(); } setWay(map,1,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(); }
八皇后問題
八皇后問題(英文:Eight queens),是由國際西洋棋棋手馬克斯·貝瑟爾於1848年提出的問題,是回溯演算法的典型案例。
問題表述為:在8×8格的國際象棋上擺放8個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。高斯認為有76種方案。1854年在柏林的象棋雜誌上不同的作者發表了40種不同的解,後來有人用圖論的方法解出92種結果。如果經過±90度、±180度旋轉,和對角線對稱變換的擺法看成一類,共有42類。計算機發明後,有多種計算機語言可以程式設計解決此問題。
回溯演算法求解八皇后問題的原則是:有衝突解決衝突,沒有衝突往前走,無路可走往回退,走到最後是答案。為了加快有無衝突的判斷速度,可以給每行和兩個方向的每條對角線是否有皇后佔據建立標誌陣列。放下一個新皇后做標誌,回溯時挪動一箇舊皇后清除標誌。
int max = 8; //使用一維陣列存放皇后可以拜訪的位置,即下標+1行皇后的位置是array[i] int[] array = new int[max]; //記錄有多少種方案 static int count; /** * * 找出所有的方案,從第一個皇后放在第一行第一列開始 * 當n=7找到合適位置列印玩方案後,會接著找別的位置,如果都衝突,就會返回到呼叫它的n=6 * 也就是第七個皇后的第一個合適位置找到以後,會接著迴圈找別的位置,以此類推,這樣就用到了回溯 * * @param n 從哪兒開始 */ public void check (int n) { if (n == max) { print(); count++; return; } for (int i = 0; i < max; i++) { array[n] = i; if (judge(n)) { check(n + 1); } } } /** * array[i] == array[n] 判斷這兩個皇后是否在同一列 * public static int abs(int a) { * return (a < 0) ? -a : a; * } * Math.abs:也就是求兩個數之間的絕對值 * 如果兩個皇后處在同一條斜線上,可以把它想象成一個直角三角形 * Math.abs(n - i)就是高 * Math.abs(array[n] - array[i])就是底 * 如果Math.abs(n - i) == Math.abs(array[n] - array[i])就是說明這兩個皇后處在同一斜線上 * * @param n 第n個皇后 * @return 是否衝突 */ //判斷新皇后的位置是否與之前的存在衝突 private boolean judge (int n) { for (int i = 0; i < n; i++) { if (array[i] == array[n] ||Math.abs(n - i) == Math.abs(array[n] - array[i])) { return false; } } return true; } //列印 public void print() { for (int item : array) { System.out.print(item + " "); } System.out.println(); }
Queue queue = new Queue(); queue.check(0); System.out.println("count = " + count);