目录
活动地址:CSDN21天学习挑战赛
一、什么是递归?
简单来说就是自己调用自己。
递归是一个非常重要的工具,是一些复杂算法的基础。很多著名问题:八皇后问题,树的遍历,斐波那契数列问题,迷宫问题等。需要认真对待。
如下,是一个阶乘的递归调用
/**
* 阶乘方法
* 输入一个数n,输出这个数的阶乘
*/
/*
* 分析思路
* 1、total = n*...*5*4*3*2*1;
* 2、a(n) = n * (a(n));
*/
import java.util.Scanner;
public class Recursion {
public static void main(String[] arg) {
System.out.println("请输入你要阶乘的数字");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a = factorial(n);
System.out.println(a);
}
public static int factorial(int n) {
int total = 1;
if(n > 1) { //递归的结束条件
total = n * factorial(n-1); //递归体if(n > 1) {
}
return total;
}
}
可以看到事实上就是把一个大的阶乘(n!)分成了(n*(n-1)!)
二、可以使用递归算法的问题的特点
1、大的问题可以分成有限数量的子问题,子问题须与原始问题为同样的事,且更为简单;子问题也可以分成有限多个子问题或者极易解决。
2、不能无限制地调用本身,须有个出口,化简为非递归状况处理。
三、经典的递归问题
1、阶乘,
/**
* 阶乘方法
* 输入一个数n,输出这个数的阶乘
*/
/*
* 分析思路
* 1、total = n*...*5*4*3*2*1;
* 2、a(n) = n * (a(n));
*/
import java.util.Scanner;
public class Recursion {
public static void main(String[] arg) {
System.out.println("请输入你要阶乘的数字");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a = factorial(n);
System.out.println(a);
}
public static int factorial(int n) {
int total = 1;
if(n > 1) { //递归的结束条件
total = n * factorial(n-1); //递归体if(n > 1) {
}
return total;
}
}
2、斐波那契数列
/**
* 斐波那契数列
* a1=1,a2=1,an=a(n-1)+a(n-2)
*/
/*
* 思路分析
* 斐波那契不同于阶乘,他的累进有两部分组成,每部分的累进又可以分成两部分。
* 累进式为 total = total(n-1) + total(n-2)
* total作为返回值,则有total = 返回值(n-1) + 返回值(n-2)
*/
import java.util.Scanner;
public class Fibonacci {
public static void main(String[] arg) {
System.out.print("n = ");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a = fib(n);
System.out.println(a);
}
private static int fib(int n) {
int total = 0;
if(n >= 3) {
total = fib(n - 1) + fib(n - 2);
}
else {
total = 1;
}
return total;
}
}
3、迷宫问题
迷宫问题
已知一个迷宫如下:
* 1、先创建一个二维数组base作为迷宫的底盘,暂定为7格,定义一个对象mouse
* 2、规定,用0,1代表地块,0代表可以走,1代表不能走
* 3、定义一个找路方法findway,它会根据参数判断向哪个方向走
* 会走到临近的、能走的通的地块,并将该地块标记2表示已走过
* 4、如果走到死路则回退一格,并将死路上的数字标记为3,表示为死路
* 5、定义一个走路方法walk,根据当前左边,传递给findway参数,根据findway回传的参数,判断前往那个方向还是回退至上一步
* 6、胜利条件:出口位置base[5][5]被标记为2
* 7、为了寻路的高效性,我们规定寻路的优先顺序为下、右、上、左
package Algorithm;
public class MiGong {
public static void main(String[] arg) {
int[][] map = new int[7][7];
for(int i = 0; i < map.length; i++){
//设立迷宫边界,(另一方面要保证每个小球能到达的位置都有上下左右)
map[0][i] = 1;
map[6][i] = 1;
map[i][0] = 1;
map[i][6] = 1;
}
//设置障碍物
map[3][1] = 1;
map[3][2] = 1;
map[3][3] = 1;
//地图的搭建
System.out.println("====迷宫的地图====");
for(int i = 0; i<map.length; i++) {
for(int j = 0; j<map.length; j++) {
System.out.print(map[i][j]);
}
System.out.print("\n");
}
//显示地图
walk(1,1,map);
}
/**
* findway为寻路方法。
* 通过改变四个方向遍历的优先级可以改变策略形成的路线
* findway方法需要接收四个方向参数,并回传下一格的参数x,y
* 若四个方向上都不能走,则回传后退参数。
*/
public static int[] findway(int down, int right, int up, int lift) {
int flag = 0;
int[] next = {0,0};
if(down == 0) {
next[0] = 1;
}
else if(right == 0) {
next[1] = 1;
}
else if(up == 0) {
next[0] = -1;
}
else if(lift == 0) {
next[1] = -1;
}
return next;
}
/**
* walk为走路方法,通过递归的方式记录都走了哪些路
* 1、接收目前的坐标,并标记3
* 2、检查是否已经到达了目的地,若是,则返回真;若否,继续;
* 3、传参给findway找路
* 4、若找不到路则返回假值
* 5、如果有路,则走;但要保留自己这一步的坐标x,y;如果没有路则返回假
* 6、若收到下一级返回的假,则继续传参给findway,找到下一条路或者找不到路为止
*/
public static boolean walk(int x, int y, int[][] map) {
map[x][y] = 2;
boolean flag = false;
int[] direction = {0,0};
if(map[5][5] == 2){
return true;
}
else {
do{
direction = findway(map[x+1][y], map[x][y+1], map[x-1][y], map[x][y-1]);
if((direction[0] == 0)&&(direction[1] == 0)) {
return false;
}
flag = walk(x+direction[0],y+direction[1],map);
}while(flag == false);
System.out.println("("+(x+direction[0])+","+(y+direction[1])+")");
return true;
}
}
}
4、汉诺塔问题
汉诺塔问题是比较典型的递归问题:
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
这么说起来比较抽象,我第一次接触汉诺塔问题是在小学课本上,第二次接触是在猩球崛起上。跟黑猩猩比智力 求解汉诺塔问题_哔哩哔哩_bilibili 这一段就是一个非常标准的四阶汉诺塔问题。
* 递归的特点是回溯,利用回溯,迷宫问题中可以完成死路时的回溯,而在汉诺塔问题中,
* 则体现在可以分层完成每个圆盘的移动,通过递归移动小一圈的圆盘,通过递归移动大一圈的圆盘。
* 分析思路:
* 1、设置三个柱子:起始柱A、辅助柱B、目标柱C,若干圆盘,从小到大依次为1到n
* 2、输入圆盘数,要求将A柱的圆盘移动到C柱,一次只能移动一个,过程中可以任意使用三根柱子,但是要保证小圆盘始终在上
* 3、输出每一个圆盘每一步移动的过程,比如:1号圆盘从 A -> C;
* 编程思路:
* 1、设置三个int变量a,b,c作为三个柱子,初始化A的柱子有圆盘1到n
* 2、进入tower函数,从第N层开始:
* 3、从第N层依次进入到第一层。移动第一层,再以此回溯移动之前的层。
* 4、先移动第i-1层,再移动自已一层,再移动第i-1层,然后结束溯回i+1层
* 5、如此循环直到第一层最后一次被移动完毕。编程的难点之一在于三个柱子身份的转换:
在向下传导的过程中:发生了目标柱与辅助柱的转换;
在完成本层的移动后,发生了起始柱与辅助柱的转换。能正确理解这两次转换就能理解汉诺塔游戏。
package Algorithm;
/**
* 汉诺塔:
* 相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。
* 该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘。
* 游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。
* 操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,
* 操作过程中盘子可以置于A、B、C任一杆上。
*/
import java.util.Scanner;
public class Hanoi {
public static void main(String[] arg) {
System.out.println("请输入汉诺塔的层数");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
char a = 'A';
char b = 'B';
char c = 'C';
tower(num,a,b,c);
}
private static void tower(int num, char a, char b, char c) {
if(num == 1) {
System.out.println(a+"->"+c);
}
else {
//在传导过程中,需要将辅助柱和目标柱转变身份。
//因为要想第i层轮盘落在c,那么i-1层就需要将目标柱设置成b
tower(num -1,a,c,b);
System.out.println(a+"->"+c);
tower(num - 1,b,a,c);//交换完i层后,i-1的起始柱就从a变成了b
}
}
}