数据结构与算法--递归
简介
定义:定义方法时,在方法内部调用方法本身,称之为递归
作用:它通常把一个大型复杂的问题,层层转换为一个与原问题相似的,规模较小的问题来求解。递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量
递归规则:
执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
方法的局部变量时独立的,不会相互受到影响
如果方法中使用的是应用类型的变量(比如数组),就会共享该引用该类型的数据
递归必须向退出递归的条件逼近,否则就是无限递归,会出现StackOverflowError
当一个方法执行完毕或者遇到return,就会返回,遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
递归模板:其中第二点,第三点的顺序可以互换,如果没有返回值,可以不用第四点
public String method(int n){
// 1.递归终止的条件
// 2.此次处理的逻辑
// 3.子问题继续递归
// 4.返回结果
}
实例
需求1:
分析:
1!: 1
2!: 2*1=2*1!
3!: 3*2*1=3*2!
4!: 4*3*2*1=4*3!
...
n!: n*(n-1)*(n-2)...*2*1=n*(n-1)!
所以,假设有一个方法factorial(n)用来求n的阶乘,那么n的阶乘还可以表示为n*recursion(n-1)
public static int recursion(int num) {
int sum = 0;
if (num < 0)
throw new IllegalArgumentException("必须为正整数!");
if (num == 1) {
//递归终止条件,跳出循环
return 1;
}
//递归计算
sum = num * recursion(num - 1);
return sum;
}
需求2:迷宫问题(回溯算法雏形)
在不考虑到达终点的路径长度时,如何在迷宫中找到一条通往终点的路径
分析:
以二维数组作为迷宫,1 表示该点为墙,0 表示该点可走(起点为 arr[1][1],终点为 arr[6][5])
每走一个点则规定一个步骤(约定 2 表示通路可以走,3 表示该点已经走过,但是走不通):
1.判断该点下面是否可以走
2.下边不行,判断该点右边是否可以走
3.右边不行,判断该点上边是否可以走
4.上边不行,判断该点左边是否可以走
5.如果都不能走,那么这个点就不能走到终点
---------------------------------------------------
迷宫图:
1 1 1 1 1 1 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 0 0 0 0 0 1
1 1 1 1 1 1 1
---------------------------------------------------
路径图:
1 1 1 1 1 1 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 0 0 1
1 0 0 2 2 2 1
1 1 1 1 1 1 1
思路如下:
- 指定一个 8×7 的二维数组当做迷宫地图 map,四周以及中间用围墙围住用 1 表示
- i,j表示从地图的 map[ i ][ j ] 位置开始出发
- 如果小球能到达指定的 map[ 6 ][ 5 ]位置,则说明通路找到
- 约定:当 map[ i ][ j ] 为 0 表示该点没有走过;为 1 表示墙;为 2 表示通路可以走;为 3 表示该点已经走过,但是走不通
- 重点是确定一个路径寻找方法:下-右-上-左(可自由定义顺序),如果该位置走不通,再进行回溯
public class FindLoad {
public static void main(String[] args) {
//构造地图
int[][] map = new int[8][7];
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
map[2][1] = 1;
map[2][2] = 1;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.printf("%s\t", map[i][j]);
}
System.out.println();
}
System.out.println("迷宫路径如下");
road(map, 1, 1);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.printf("%s\t", map[i][j]);
}
System.out.println();
}
}
/***
* 递归寻找迷宫出口的路径
* @param arr 迷宫地图
* @param x 某个点的横坐标
* @param y 某个点的纵坐标
* @return
*/
public static boolean road(int[][] arr, int x, int y) {
if (arr[6][5] == 2) {
//到达目的地
return true;
} else {
if (arr[x][y] == 0) {
//该点可走得通
arr[x][y] = 2;
if (road(arr, x + 1, y)) {
//判断该点下边是否可行
return true;
} else if (road(arr, x, y + 1)) {
//判断该点右边是否可行
return true;
} else if (road(arr, x - 1, y)) {
//判断该点上边是否可行
return true;
} else if (road(arr, x, y - 1)) {
//判断该点左边是否可行
return true;
} else{
//该路经走不通
arr[x][y] = 3;
return false;
}
} else {
//该点走不通
return false;
}
}
}
}