递归:递归就是在函数里调用函数本身。 基线条件和递归条件 1、基线条件:就是指结束调用的条件,避免形成无限循环。 2、递归条件:就是指函数调用自己的条件。 3、递归必须向基线条件逼近,否则就会出现无线递归。
一、打印问题
给出一个整数,使用递归打印它。
public class Recursion {
public static void main(String[] args) {
Recursion.test(4);
}
public static void test(int a) {
if (a > 2) {
test(a-1);
}
System.out.println(a);
}
}
解读:
第一次调用test,创建一个栈,传入4,判断4是否会大于2,4大于2进行第二次调用,4-1
第二次调用test,创建一个栈,传入3,判断3是否会小于2,3大于2进行第三次调用,3-1
第三从调用test,创建一个栈,传入2,判断2是否会大于2,2等于2进行返回
第一次返回,销毁栈,打印2,
第二次返回,销毁栈,打印3
第三次返回,销毁栈,打印4
结束程序
2
3
4
二、累加问题
给出一个整数,使用递归求出它累加的和。
public class Accumulation {
public static void main(String[] args) {
int a = Accumulation.test(3);
System.out.println(a);
}
public static int test(int x) {
if (x == 1) {
return 1;
} else {
return x+test(x-1);
}
}
}
解读:
1.在main中第一次调用test,创建第一层栈,传入3,判断是否等于1,3大于1,符合递归条件,进行第二次调用test,3-1
2.第二次调用test,创建第二层栈,传入2,判断是否等于1,2大于1,符合递归条件,进行第三次调用test,2-1
3.第三次调用test,创建第三层栈,传入1,判断是否等于1,1等于1,符合基线条件,进行返回
4.第三层栈,返回1
5.第二层栈,返回3,2+1
6.第一层栈,返回6,3+2+1
程序结束
6
三、阶层问题
给出一个整数,使用递归求出它的阶乘。
public class Factorial {
public static void main(String[] args) {
int a = Factorial.test(5);
System.out.println(a);
}
public static int test(int x){
if (x == 1){ // 基线条件
return 1;
}else{ // 递归条件
return test(x-1) * x;
}
}
}
递归解读:
1. 在main中第一次调用test,创建第一层栈,传入5,判断是否等于1,5大于1,符合递归条件,进行第二次调用test,5-1
2. 第二次调用test,创建第二层栈,传入4,判断是否等于1,4大于1,符合递归条件,进行第三次调用test,4-1
3. 第三次调用test,创建第三层栈,传入3,判断是否等于1,3大于1,符合递归条件,进行第四次调用test,3-1
4. 第四次调用test,创建第四层栈,传入2,判断是否等于1,2大于1,符合递归条件,进行第五次调用test,2-1
5. 第五次调用test,创建第五层栈,传入1,判断是否等于1,1等于1,符合基线条件,进行返回
6. 第五层栈,销毁栈,返回1,return 1;
7. 第四层栈,销毁栈,返回2,return test(2-1) * 2;
8. 第三层栈,销毁栈,返回6,return test(3-1) * 3;
9. 第二层栈,销毁栈,返回24,return test(4-1) * 4;
10. 第一层栈,销毁栈,返回120,return test(24-1) * 5;
11. 程序结束
120
四、斐波那契
用递归求出斐波那契数1,1,2,3,4,5,8,13...给一个整数x,求出它的值是多少。
public class Fibonacci {
public static void main(String[] args) {
int x = 4;
int a = Fibonacci.test(x);
if (a != -1){
System.out.println("当x="+x+",对应的斐波那契是"+a);
}
}
//思路分析:斐波那契是前两个数之和,且必须是整数。
public static int test(int x) {
if (x >= 1) { //斐波那契必须是整数
if (x == 1 || x == 2) { //
return 1;
} else {
return test(x - 1) + test(x - 2);
}
} else {
System.out.println("要求输入的x必须是大于等于1的整数");
return -1;
}
}
}
解读:
第一次调用test方法,向方法传递整数x(x=4)
判断x是否大于等于1(因为斐波那契数不能小于1),小于1,输出提示信息,并返回-1
大于等于1,则在判断x是否是1或2,是1或2则返回1;(因为1和2的斐波那契数是1)
否则进行第二次第一个调用test,创建栈,传入3 + 第二次第二个调用test,创建栈,传入2
第二次第一个test,进行x是否大于等于1的判断,3>=1,在判断是否会等于1或2,3不等于1或2,
进行第三次第一个调用test,创建栈,传入2,+ 第三次第二个调用test,创建栈,传入1
第三次第一个test,进行x是否大于等于1的判断,2>=1,在判断是否会等于1或2,2等于1或2,进行返回
第三次第二个test,进行x是否大于等于1的判断,1>=1,在判断是否会等于1或2,1等于1或2,进行返回
第二次第二个test,进行x是否大于等于1的判断,2>=1,在判断是否会等于1或2,2等于1或2,进行返回
第三次第一个test,返回,1
第三次第二个test,返回1
第二次第一个test,返回2
第二次第二个test,返回1
第一次返回调用处,返回3
3
五、猴子吃桃问题
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个。以后的每天猴子都吃其中的一半,然后再多吃一个。
当第10天时,想再吃时(还没有吃),发现只有一个桃子了。问最初有多少个桃子。
public class MonkeyPeach {
public static void main(String[] args) {
int day = 1;
int a = MonkeyPeach.test(day);
if (a != -1) {
System.out.println("第" + day + "天有" + a + "个桃");
}
}
public static int test(int day) {
if (day == 10) { // 第十天只有1个桃
return 1;
} else if (day >= 1 && day <= 9) { //天数必须在1-10天之内
return (test(day + 1) + 1) * 2;
} else {
System.out.println("day必须在1-10之间");
return -1;
}
}
}
思路分析:逆推
1. day10 有 1个桃子
2. day9 有 (day10+1)*2=4 个桃子 4/2-1=1
3. day8 有 (day9+1)*2=10 个桃子 10/2-1=4
规律就是 前一天的桃子 = (后一天的桃子 +1)*2
输入天数可以获取当天桃子的个数,输入6
第一次调用test(6),创建栈,出传入6,判断是否等于10,6不等于10,在判断是否大于等于1且小于等于9,6大于等于1且小于等于9,进行第二次调用test
第二次调用test(6+1),创建栈,传入7,判断是否等于10,7不等于10,在判断是否大于等于1且小于等于9,7大于等于1且小于等于9,进行第三次调用test
第三次调用test(7+1),创建栈,传入8,判断是否等于10,8不等于10,在判断是否大于等于1且小于等于9,8大于等于1且小于等于9,进行第四次调用test
第四次调用test(8+1),创建栈,传入9,判断是否等于10,9不等于10,在判断是否大于等于1且小于等于9,9大于等于1且小于等于9,进行第五次调用test
第五次调用test(9+1),创建栈,传入10,判断是否等于10,10等于10,进行返回,返回1
第五次调用,return 1,返回1,销毁栈
第四次调用,return (1 + 1) * 2,返回4,销毁栈
第五次调用,return (4 + 1) * 2,返回10,销毁栈
第五次调用,return (10 + 1) * 2,返回22,销毁栈
第五次调用,return (22 + 1) * 2,返回46,销毁栈
程序结束
46
六、汉诺塔问题
把圆盘从a塔移动到c塔,且小于圆盘要在上面。
public class HanoiTower {
public static void main(String[] args) {
Test test = new Test();
test.move(5,'A', 'B', 'C');
}
}
class Test{
// num 表示要移动的个数,a,b,c 分别表示A塔 B塔 C塔
public static void move(int num, char a, char b, char c){
// 如果只有一个圆盘
if (num == 1){
System.out.println(a + "->" + c);
}else{
// 如果有多个圆盘,可以看成两个,最下面的和上面的所有圆盘(num-1)
//(1)先移动上面所有的圆盘到b,借助c
move(num-1, a, c, b);
//(2)把最下面的这个圆盘,移动到c
System.out.println(a + "->" + c);
//(3)再把b塔的所有圆盘,移动到c,借助a
move(num-1, b, a, c);
}
}
}
汉诺塔解读:
main创建第一层栈,第一次调用move,创建第二层栈,传入 3,A,B,C,判断3是否等于1,3不等于1,
则进行第二次调用move,创建第三层栈,传入 2,A,C,B,判断2是否等于1,2不等于1
则进行第三次调用move,创建第四层栈,传入 1,A,B,C,判断1是否等于1,1等于1,输出 A->C,销毁栈,返回第三层
第三层栈,输出 A->B,
第四次调用move,创建第四层栈,传入1,C,A,B,判断1是否等于1,1等于1,输出 C->B,销毁栈,返回第三层
销毁栈,返回到第二层
第二层栈,输出 A->C,
第二层栈,进行第五次调用move,创建第三层栈,传入2,B,A,C,判断2是否等于1,2不等于1
则进行第六次调用move,创建第四层栈,传入 1,B,C,A,判断1是否等于1,1等于1,输出 B->A,销毁栈,返回第三层
第三层栈,输出B->C
第三层,进行第七次调用move,创建第四层栈,传入1,C,B,A,判断1是否等于1,1等于1,输出 A->C,销毁栈,返回第三层
销毁栈,返回第二层
销毁栈,返回第一层调用处
结束程序
A->C
A->B
C->B
A->C
B->A
B->A
C->A
▲表示输出
七、老鼠出迷宫问题
public class Maze {
public static void main(String[] args) {
//创建迷宫,用二维数组表示 int[][] map = new int[8][7]
//先规定 map数组的元素值:0表示可以走,1表示障碍物
int[][] map = new int[8][7];
// 设置障碍物
//将最上面的一行和最下面的一行,全部设置为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;
// 输出地图
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给老鼠找路
test t = new test();
t.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();
}
}
}
// 使用递归回溯解决老鼠出迷宫
// 1. findWay方法就是专门来找迷宫路径的
// 2. 如果找到返回true,没有找到返回false
// 3. map 就是二维数组,即表示迷宫
// 4. x,y就是老鼠的坐标,初始化为 (1, 1)
// 5. 因为是递归找路,所以先规定 map数组的各个值的含义
// 0表示可以走,1表示障碍物,2表示可以走,3表示走过,但走不通是死路
// 6. 当map[6][5]=2 就说明找到通路,就可以结束,否则就继续找。
// 7. 先确定老鼠找路策略 下-> 右-> 上-> 左
class test {
public boolean findWay(int[][] map, int x, int y) {
if (map[6][5] == 2) { //说明已经找到
return true;
} else {
if (map[x][y] == 0) { //当前这个位置0,说明表示可以走
// 我们可以假定走通
map[x][y] = 2;
// 使用找路策略,来确定该位置是否真的可以走通
// 下-> 右-> 上-> 左
if (findWay(map, x + 1, y)) { //下
return true;
} else if (findWay(map, x, y + 1)) { //右
return true;
} else if (findWay(map, x - 1, y)) { //上
return true;
} else if (findWay(map, x, y - 1)) { //左
return true;
} else {
map[x][y] = 3;
return false;
}
} else { //map[x][y] = 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 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