你们好,我是阿濠,今篇内容跟你们分享的是算法之递归,很高兴分享到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...