方法自己调用自己,每次调用的时候传入不同的变量(有助于解决复杂问题,让代码简洁)
重要规则:
1.执行一个方法的时候就会创建一个新的受保护的空间(栈空间)
2.方法的局部变量独立,不会相互影响
3.如果方法中使用的是引用数据类型(数组,对象),就会共享该引用类型的数据
【如果是引用数据类型,方法中传入的形参就是地址,这些形参都可以通过这个地址影响堆里面的同一个空间】
4.递归必须向退出递归的条件逼近,否则无限递归【StackOverFlowError,栈溢出】
5.一个方法执行完毕或者遇到return,就会返回(谁调用就将结果返回给谁),方法执行完毕或者遇到return,该方法就结束
每次需要递归的时候都会出现一个新栈,新栈执行完之后才执行旧栈(所以最先输出的结果就是最最新的栈的结果,以此类推)
假设改一下方法体:
改完之后结果只会出现一个n=2
why?(最外层【第一层】方法执行的时候满足n>2,所以执行递归(不执行else里面的输出语句),第二层递归的时候也满足n>2,所以执行第三层递归......以此类推,一直执行递归,直到出现n=2不满足n>2的情况,这时候就执行else里面的输出语句,输出n=2,因为前面几层递归都是执行下一层递归,所以不会出现n=3、4、5、6.。。。的情况)
阶乘
//阶乘(factorical)
public int factorical(int n){
if(n == 1){
return 1;
}else{
return factorical(n - 1)*n;
}
}
从最顶层的栈得到的确定值依次往下,5*4*3*2*1 = 120
练习:
①斐波那契数(后一个数字是前两个数字之和)
class t{
public int feibo(int n){
if(n==1 || n ==2){
return 1
}else{
return feibo(n-1)+feibo(n-2);
}
}
}
可以稍微改进(做一个数据保护)
class t{
public int feibo(int n){
if(n>0){
if(n==1 || n ==2){
return 1;
}else{
return feibo(n-1)+feibo(n-2);
}
}else{
System.out.println("请输入一个大于0的数字");
return -1;
}
}
}
②猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个! 以后每天猴子都吃其中的一半,然后再多吃一个。当到第 10 天时, 想再吃时(即还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
(规律:前一天的桃子数量=后一天桃子数量+1再*2)
//猴子吃桃
public int tao(int day){//day表示倒数第几天
if(day==1){
return 1;
}else{
return (tao(day-1)+1)*2;
}
}
法二:
public int tao2(int day){//day表示正数第几天
if(day == 10){
return 1;
}else if(day >=1 && day <= 9){
return (tao2(day + 1)+1)*2;
}else{
return -1;
}
}
结果都是1534,两种方法都没问题(重要的是找规律)
迷宫问题(小球从左上角走到右上角,躲避障碍物)
public class migong{
public static void main(String[] args) {
//用二维数组表示迷宫
//元素值0可以走,1表示障碍物
int[][] map = new int[8][7];//8行7列的迷宫
//将最上面最下面两行都设为1(表示不能走)
for(int i = 0;i<7;i++){
map[0][i] = 1;
map[7][i] = 1;
}
//左右两列设为1(每列有8行,所以i<8)
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<map.length;i++){//二维数组的长度是行数
for(int j = 0;j<map[i].length;j++){
System.out.print(map[i][j]+" ");
}
System.out.println();
}
//使用findway找路
t t1 = new t();
t1.findway(map,1,1);
System.out.println("找路后的地图情况:");
for(int i = 0;i<map.length;i++){//二维数组的长度是行数
for(int j = 0;j<map[i].length;j++){
System.out.print(map[i][j]+" ");
}
System.out.println();
}
}
}
class t{
//递归思想求解
//findway这个方法用来找迷宫路径
//找到返回true,找不到返回false
//map是二维数组,表示迷宫
//i,j表示位置,初始位置是(1,1)
//规定数组值的含义,元素值0可以走,1表示障碍物,2可以走,3走过但是走不通
//当map[6][5] = 2,说明找到路了,否则继续找
//确定找路的策略,下->右->上->左
public 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;//,假定能走通,其实走不通,所以就赋值为3
return false;
}
}else{
return false;
}
}
}
}
(结果里面2就是小球的路径)
findway方法里面,当我们把数组,i,j传进来之后,数组里的元素除了外围一周和内两个也定的元素是1之外其他都是0,这时候map[6][5]是不等于2的,这时候就进入到下面的else,我们调用方法传入的i和j都是1,这时候map[1][1] = 0成立,进入if语句,我们先假设可以走通,先把当前位置改为2,然后我们让他往下走(这个所谓的往下走其实就是运用递归重新调用一下自己这个方法,只不过把传进去的参数改成起始位置往下的一个元素),然后在看这个元素满足if-else里面的那个条件,往下走不了就往右.....以此类推,直到终点位置变成2或者返回false(走不通)
为啥调用两个不同的方法输出是一样的结果?
先调用哪个方法,下面的就跟着上面的一样?
原因:调用findway2的时候,传入的map时已经被第一次调用findway
的时候改过的地图,改过的地图map[6][5]==2,所以直接执行if语句,else里面的内容就不执行了
测试回溯现象
回溯:比如把起始位置右下角挡起来,小球按照下右上左的顺序走到起始位置下面的时候,发现再按照下下右上左的顺序走是走不通的,这时候这个位置就会变成3,这时候就会返回到起始位置,回到起始位置之后会发现起始位置下面走不通,所以就会向右走 【在某一个位置四个方向都走不通的时候就会回到上一个栈,根据刚才已经走得方向的下一个方向进行探测】