递归概念和调用机制
递归的概念
简单来说:递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。
递归的调用机制
- 当程序执行到一个方法时,就会开辟一个独立的空间(栈);
- 每个空间的数据(局部变量)是独立的;
代码演示
1 2 3 4 5 6 7 8 9 10
| 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); }
|
运行结果
1 2 3 4 5
| n=2 n=3 n=4
Process finished with exit code 0
|
递归能解决的问题和规则
递归解决什么样的问题
- 数学问题:八皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题;
- 各种算法中也会使用递归,例如:快排,归并排序,二分查找,分治算法等;
- 将用栈解决的问题->用递归代码比较简洁;
递归需要遵守的重要规则
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间);
- 方法的局部变量是独立的,不会相互影响;
- 如果方法中使用的是引用类型变量,就共享该引用类型的数据;
- 递归必须向退出递归的条件逼近,否则就是无线递归(最后出现栈溢出);
- 当一个方法执行完毕,或者遇到
return
,就会返回;遵守谁调用,就将结果返回给谁,当方法执行完毕或者返回时,该方法也执行完毕;
迷宫回溯问题思路分析
迷宫问题思路分析
- 小球得到的路径,和程序员设置的找路策略有关,和找路的上下左右的顺序相关;
- 再得到小球路径时,可以先使用(下右上左)策略,再改为(上右下左)策略,看看路径是不是有变化;
- 测试回溯现象
迷宫回溯问题代码实现
下右上左测试
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| package com.jokerdig.recursion;
public class Maze { public static void main(String[] args) { int [][] map = new int [8][7]; for (int i = 0; i < 7; i++) { map[0][i] = 1; map[7][i] = 1; } for (int i = 0; i < 8; i++) { map[i][0] = 1; map[i][6] = 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(); } 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(); } }
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; } } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| =======地图的情况======= 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 1 1 1 1 1 1 1 2 0 0 0 0 1 1 2 2 2 0 0 1 1 1 1 2 0 0 1 1 0 0 2 0 0 1 1 0 0 2 0 0 1 1 0 0 2 2 2 1 1 1 1 1 1 1 1
Process finished with exit code 0
|
测试回溯
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| package com.jokerdig.recursion;
public class Maze { public static void main(String[] args) { int [][] map = new int [8][7]; for (int i = 0; i < 7; i++) { map[0][i] = 1; map[7][i] = 1; } for (int i = 0; i < 8; i++) { map[i][0] = 1; map[i][6] = 1; } map[3][1] = 1; map[3][2] = 1; map[1][2] = 1; map[2][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(); } 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(); } }
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; } } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| =======地图的情况======= 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 0 1 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 1 1 1 1 1 1 1 3 1 0 0 0 1 1 3 1 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
Process finished with exit code 0
|
上右下左测试
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| package com.jokerdig.recursion;
public class Maze { public static void main(String[] args) { int [][] map = new int [8][7]; for (int i = 0; i < 7; i++) { map[0][i] = 1; map[7][i] = 1; } for (int i = 0; i < 8; i++) { map[i][0] = 1; map[i][6] = 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(); } setWayUpdate(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(); } }
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; } } } public static boolean setWayUpdate(int[][] map,int i,int j){ if(map[6][5] == 2){ return true; }else{ if(map[i][j]==0){ map[i][j] = 2; if(setWayUpdate(map,i-1,j)){ return true; }else if(setWayUpdate(map,i,j+1)){ return true; }else if(setWayUpdate(map,i+1,j)){ return true; }else if(setWayUpdate(map,i,j-1)){ return true; }else{ map[i][j] = 3; return false; } }else{ return false; } } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| =======地图的情况======= 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 1 1 1 1 1 1 1 2 2 2 2 2 1 1 0 0 0 0 2 1 1 1 1 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 0 0 0 0 2 1 1 1 1 1 1 1 1
Process finished with exit code 0
|
八皇后问题思路分析
八皇后问题介绍
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋手马克斯·贝瑟尔于1848年提出:再8×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列;
八皇后问题代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package com.jokerdig.recursion;
public class EightEmpresses { int max = 8; int[] array = new int[max]; static int count = 0; public static void main(String[] args) { EightEmpresses empresses = new EightEmpresses(); empresses.check(0); System.out.println("八皇后问题一共有"+count+"种正确摆法"); } private void check(int n){ if(n==max){ print(); return; } for (int i = 0; i < max; i++) { array[n] = i; if(judge(n)){ check(n+1); } } }
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; } private void print(){ count++; for (int i = 0; i < array.length; i++) { System.out.print(array[i]+ " "); } System.out.println(); } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| 0 4 7 5 2 6 1 3 0 5 7 2 6 3 1 4 0 6 3 5 7 1 4 2 0 6 4 7 1 3 5 2 1 3 5 7 2 0 6 4 1 4 6 0 2 7 5 3 1 4 6 3 0 7 5 2 1 5 0 6 3 7 2 4 1 5 7 2 0 3 6 4 1 6 2 5 7 4 0 3 1 6 4 7 0 3 5 2 1 7 5 0 2 4 6 3 2 0 6 4 7 1 3 5 2 4 1 7 0 6 3 5 2 4 1 7 5 3 6 0 2 4 6 0 3 1 7 5 2 4 7 3 0 6 1 5 2 5 1 4 7 0 6 3 2 5 1 6 0 3 7 4 2 5 1 6 4 0 7 3 2 5 3 0 7 4 6 1 2 5 3 1 7 4 6 0 2 5 7 0 3 6 4 1 2 5 7 0 4 6 1 3 2 5 7 1 3 0 6 4 2 6 1 7 4 0 3 5 2 6 1 7 5 3 0 4 2 7 3 6 0 5 1 4 3 0 4 7 1 6 2 5 3 0 4 7 5 2 6 1 3 1 4 7 5 0 2 6 3 1 6 2 5 7 0 4 3 1 6 2 5 7 4 0 3 1 6 4 0 7 5 2 3 1 7 4 6 0 2 5 3 1 7 5 0 2 4 6 3 5 0 4 1 7 2 6 3 5 7 1 6 0 2 4 3 5 7 2 0 6 4 1 3 6 0 7 4 1 5 2 3 6 2 7 1 4 0 5 3 6 4 1 5 0 2 7 3 6 4 2 0 5 7 1 3 7 0 2 5 1 6 4 3 7 0 4 6 1 5 2 3 7 4 2 0 6 1 5 4 0 3 5 7 1 6 2 4 0 7 3 1 6 2 5 4 0 7 5 2 6 1 3 4 1 3 5 7 2 0 6 4 1 3 6 2 7 5 0 4 1 5 0 6 3 7 2 4 1 7 0 3 6 2 5 4 2 0 5 7 1 3 6 4 2 0 6 1 7 5 3 4 2 7 3 6 0 5 1 4 6 0 2 7 5 3 1 4 6 0 3 1 7 5 2 4 6 1 3 7 0 2 5 4 6 1 5 2 0 3 7 4 6 1 5 2 0 7 3 4 6 3 0 2 7 5 1 4 7 3 0 2 5 1 6 4 7 3 0 6 1 5 2 5 0 4 1 7 2 6 3 5 1 6 0 2 4 7 3 5 1 6 0 3 7 4 2 5 2 0 6 4 7 1 3 5 2 0 7 3 1 6 4 5 2 0 7 4 1 3 6 5 2 4 6 0 3 1 7 5 2 4 7 0 3 1 6 5 2 6 1 3 7 0 4 5 2 6 1 7 4 0 3 5 2 6 3 0 7 1 4 5 3 0 4 7 1 6 2 5 3 1 7 4 6 0 2 5 3 6 0 2 4 1 7 5 3 6 0 7 1 4 2 5 7 1 3 0 6 4 2 6 0 2 7 5 3 1 4 6 1 3 0 7 4 2 5 6 1 5 2 0 3 7 4 6 2 0 5 7 4 1 3 6 2 7 1 4 0 5 3 6 3 1 4 7 0 2 5 6 3 1 7 5 0 2 4 6 4 2 0 5 7 1 3 7 1 3 0 6 4 2 5 7 1 4 2 0 6 3 5 7 2 0 5 1 4 6 3 7 3 0 2 5 1 6 4 八皇后问题一共有92种正确摆法
Process finished with exit code 0
|