1、学习不用心,骗人又骗己;
2、学习不刻苦,纸上画老虎;
3、学习不惜时,终得人耻笑;
4、学习不复习,不如不学习;
5、学习不休息,毁眼伤身体;
7、狗才等着别人喂,狼都是自己寻找食物;
简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量。递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。
package com.zb.ds;
//递归
public class Recursion {
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);
}
}
n=2
n=3
n=4
package com.zb.ds;
//递归
public class Recursion {
public static void main(String[] args) {
test(4);
System.out.println(test1(5));
}
//打印问题
public static void test(int n) {
if (n > 2) {
test(n - 1);
}
System.out.println("n=" + n);
}
//阶乘问题
public static int test1(int n){
if(n==1){
return 1;
}else {
return test1(n-1) * n;
}
}
}
n=2
n=3
n=4
120
test1(4)*5 —— test1(3)*4*5 —— test1(2)*3*4*5 —— test1(1)*2*3*4*5 —— 1*2*3*4*5
各种数学问题如:8皇后问题、汉诺塔、阶乘问题、迷宫问题、球和篮子的问题(google编程大赛);
各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等;
将用栈解决的问题-->递归代码比较简洁;
①当程序执行到一个方法时,就会开辟一个独立的空间(栈);
②每个空间的数据(局部变量)是相互独立的;
③如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据;
④递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,死龟了。。。
⑤当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕;
①小球得到的路径,和程序员设置的找路策略有关即:找路的上下左右的顺序相关;
②再得到小球路径时,可以先使用(下右上左),再改成(上右下左),看看路径是不是有变化;
③测试回溯现象;
④思考: 如何求出最短路径?
package com.zb.ds;
//递归:迷宫问题
public class MiGong {
public static void main(String[] args) {
//创建一个二维数组,模拟迷宫
//地图
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;
}
//挡板
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();
}
//找路
if(findWay(map, 1, 1)){
System.out.println("找到路了!");
}else {
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、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 findWay(int[][] map,int i,int j){
if(map[6][5]==2){
return true;
}else {
if(map[i][j]==0){
//没走过,按策略走:下——右——上——左
map[i][j] = 2;//假定此点可以走通
if(findWay(map,i+1,j)){
//向下走
return true;
}else if(findWay(map,i,j+1)){
//向右走
return true;
}else if(findWay(map,i-1,j)){
//向上走
return true;
}else if(findWay(map,i,j-1)){
//向左走
return true;
}else {
map[i][j] = 3;
return false;
}
}else{//如果map[i][j]!=0,可能是1,2,3
return false;
}
}
}
}
(找到路了,2是路线,这个算法真是太强大了)
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
我们可以知道,小球得到的路径和程序员设置的找路策略有很大关系,我们来讲原来的(下右上左)改成(上右下左),进行测试,看结果如何;
(说明:只需要将i的+1和-1呼唤即可)
package com.zb.ds;
//递归:迷宫问题
public class MiGong {
public static void main(String[] args) {
//创建一个二维数组,模拟迷宫
//地图
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;
}
//挡板
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();
}
//找路
if(findWay(map, 1, 1)){
System.out.println("找到路了!");
}else {
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、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 findWay(int[][] map,int i,int j){
if(map[6][5]==2){
return true;
}else {
if(map[i][j]==0){
//没走过,按策略走:下——右——上——左
//更改策略为:上——右——下——左
//说明:只需要将+1和-1呼唤即可
map[i][j] = 2;//假定此点可以走通
if(findWay(map,i-1,j)){
//向下走
return true;
}else if(findWay(map,i,j+1)){
//向右走
return true;
}else if(findWay(map,i+1,j)){
//向上走
return true;
}else if(findWay(map,i,j-1)){
//向左走
return true;
}else {
map[i][j] = 3;
return false;
}
}else{//如果map[i][j]!=0,可能是1,2,3
return false;
}
}
}
}
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
(搁置,我自己的想法还不够成熟,暂时不做实现!)
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法?【92种】
①第一个皇后先放第一行第一列;
②第二个皇后放在第二行第一列、然后判断是否OK(是否冲突), 如果不OK,继续放在第二列、第三列、依次把所有列都放完,找到一个合适;
③继续第三个皇后,还是第一列、第二列……直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确解;
④当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,全部得到;
⑤然后回头继续第一个皇后放第二列,后面继续循环执行①②③④的步骤;
理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,用一个一维数组即可解决问题. arr[8] = {0 , 4, 7, 5, 2, 6, 1, 3} //对应arr 下标 表示第几行,即第几个皇后,arr[i] = val , val 表示第i+1个皇后,放在第i+1行的第val+1列;
package com.zb.ds;
//递归:八皇后问题(回溯算法)
public class EightQueen {
//定义一个max表示有多少个皇后
int max = 8;
int count = 0;
//定义一个数组array,保存皇后放置位置的结果,比如:arr[8] = {0 , 4, 7, 5, 2, 6, 1, 3}
int[] array = new int[max];
public static void main(String[] args) {
//测试八皇后
new EightQueen().check(0);
}
//编写一个方法,放置第n个皇后
//每一次递归都有一套for循环,所有也有了从后往前回溯,成立的话摆下一个皇后,不成立的话将当前皇后摆到下一列继续判断,
// 假如当前行都不成立,回到上一行进行如此循环的判断,直到全部走通,简直amazing
private void check(int n){
if (n==max){//n==8,已经放了8个皇后
print();
return;
}
//依次放入皇后,并判断是否冲突
for (int i = 0; i < max; i++) {
//先把当前皇后n,放到改行的第1列
array[n] = i;
//判断当放置第n个皇后到i列时,是否冲突
if(judge(n)){//不冲突
//接着放n+1和皇后,开始递归
check(n+1);
}
//如果冲突就继续执行循环,放到下一列
}
}
//当我们放置第n个皇后去检测是否与前面摆放的皇后有冲突
//n表示第n个皇后,i表示n前面的某个皇后,array[n]表示第n个皇后的位置,array[i]表示n前面的某个皇后的位置
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])){
//在同一列或者在同一斜线上
//在同一斜线上:Math.abs(n-i)表示相差的列数,Math.abs(array[n] - array[i])表示相差的行数,如果二者相等,说明在一条斜线上
return false;
}
}
return true;
}
//写一个方法,可以讲皇后摆放的位置打印出来
private void print(){
count++;
System.out.print("第" + count + "种解法:");
for (int value : array) {
System.out.print(value + " ");
}
System.out.println();
}
}
第1种解法:0 4 7 5 2 6 1 3
第2种解法:0 5 7 2 6 3 1 4
第3种解法:0 6 3 5 7 1 4 2
第4种解法:0 6 4 7 1 3 5 2
第5种解法:1 3 5 7 2 0 6 4
第6种解法:1 4 6 0 2 7 5 3
第7种解法:1 4 6 3 0 7 5 2
第8种解法:1 5 0 6 3 7 2 4
第9种解法:1 5 7 2 0 3 6 4
第10种解法:1 6 2 5 7 4 0 3
第11种解法:1 6 4 7 0 3 5 2
第12种解法:1 7 5 0 2 4 6 3
第13种解法:2 0 6 4 7 1 3 5
第14种解法:2 4 1 7 0 6 3 5
第15种解法:2 4 1 7 5 3 6 0
第16种解法:2 4 6 0 3 1 7 5
第17种解法:2 4 7 3 0 6 1 5
第18种解法:2 5 1 4 7 0 6 3
第19种解法:2 5 1 6 0 3 7 4
第20种解法:2 5 1 6 4 0 7 3
第21种解法:2 5 3 0 7 4 6 1
第22种解法:2 5 3 1 7 4 6 0
第23种解法:2 5 7 0 3 6 4 1
第24种解法:2 5 7 0 4 6 1 3
第25种解法:2 5 7 1 3 0 6 4
第26种解法:2 6 1 7 4 0 3 5
第27种解法:2 6 1 7 5 3 0 4
第28种解法:2 7 3 6 0 5 1 4
第29种解法:3 0 4 7 1 6 2 5
第30种解法:3 0 4 7 5 2 6 1
第31种解法:3 1 4 7 5 0 2 6
第32种解法:3 1 6 2 5 7 0 4
第33种解法:3 1 6 2 5 7 4 0
第34种解法:3 1 6 4 0 7 5 2
第35种解法:3 1 7 4 6 0 2 5
第36种解法:3 1 7 5 0 2 4 6
第37种解法:3 5 0 4 1 7 2 6
第38种解法:3 5 7 1 6 0 2 4
第39种解法:3 5 7 2 0 6 4 1
第40种解法:3 6 0 7 4 1 5 2
第41种解法:3 6 2 7 1 4 0 5
第42种解法:3 6 4 1 5 0 2 7
第43种解法:3 6 4 2 0 5 7 1
第44种解法:3 7 0 2 5 1 6 4
第45种解法:3 7 0 4 6 1 5 2
第46种解法:3 7 4 2 0 6 1 5
第47种解法:4 0 3 5 7 1 6 2
第48种解法:4 0 7 3 1 6 2 5
第49种解法:4 0 7 5 2 6 1 3
第50种解法:4 1 3 5 7 2 0 6
第51种解法:4 1 3 6 2 7 5 0
第52种解法:4 1 5 0 6 3 7 2
第53种解法:4 1 7 0 3 6 2 5
第54种解法:4 2 0 5 7 1 3 6
第55种解法:4 2 0 6 1 7 5 3
第56种解法:4 2 7 3 6 0 5 1
第57种解法:4 6 0 2 7 5 3 1
第58种解法:4 6 0 3 1 7 5 2
第59种解法:4 6 1 3 7 0 2 5
第60种解法:4 6 1 5 2 0 3 7
第61种解法:4 6 1 5 2 0 7 3
第62种解法:4 6 3 0 2 7 5 1
第63种解法:4 7 3 0 2 5 1 6
第64种解法:4 7 3 0 6 1 5 2
第65种解法:5 0 4 1 7 2 6 3
第66种解法:5 1 6 0 2 4 7 3
第67种解法:5 1 6 0 3 7 4 2
第68种解法:5 2 0 6 4 7 1 3
第69种解法:5 2 0 7 3 1 6 4
第70种解法:5 2 0 7 4 1 3 6
第71种解法:5 2 4 6 0 3 1 7
第72种解法:5 2 4 7 0 3 1 6
第73种解法:5 2 6 1 3 7 0 4
第74种解法:5 2 6 1 7 4 0 3
第75种解法:5 2 6 3 0 7 1 4
第76种解法:5 3 0 4 7 1 6 2
第77种解法:5 3 1 7 4 6 0 2
第78种解法:5 3 6 0 2 4 1 7
第79种解法:5 3 6 0 7 1 4 2
第80种解法:5 7 1 3 0 6 4 2
第81种解法:6 0 2 7 5 3 1 4
第82种解法:6 1 3 0 7 4 2 5
第83种解法:6 1 5 2 0 3 7 4
第84种解法:6 2 0 5 7 4 1 3
第85种解法:6 2 7 1 4 0 5 3
第86种解法:6 3 1 4 7 0 2 5
第87种解法:6 3 1 7 5 0 2 4
第88种解法:6 4 2 0 5 7 1 3
第89种解法:7 1 3 0 6 4 2 5
第90种解法:7 1 4 2 0 6 3 5
第91种解法:7 2 0 5 1 4 6 3
第92种解法:7 3 0 2 5 1 6 4