递归的用途:
1)
各种数学问题如
:
八皇后问题
,
汉诺塔
,
阶乘问题
,
迷宫问题
,
球和篮子的问题
(google
编程大赛
)
2)
各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等
.
3)
将用栈解决的问题
-->递
归代码比较简洁
递归需要遵守的重要规则:
1)
执行一个方法时,就创建一个新的受保护的独立空间
(
栈空间
)
2)
方法的局部变量是独立的,不会相互影响
,
比如
n
变量
3)
如果方法中使用的是引用类型变量
(
比如数组
)
,就会共享该引用类型的数据
.
4)
递归必须向退出递归的条件逼近,否则就是无限递归
,
出现
StackOverflowError
,死龟了
:)
5)
当一个方法执行完毕,或者遇到
return
,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
迷宫问题:
假设现在有这样的一个迷宫,1为边界,X 为终点。我们从不同的起点出发,采用不同的策略(上下左右)会得到不同的结果。我们希望能够进行随机游走,尽可能地找出最短路径。
1 | 1 | 1 | 1 | 1 | 1 |
1 | 1 | ||||
1 | 1 | 1 | |||
1 | 1 | 1 | |||
1 | X | 1 | |||
1 | 1 | 1 | 1 | 1 | 1 |
思路:利用随机数使得每次游走的方向都是随机的,每次成功到达终点的路径长度与对应的路径以key-value的形式保存在hashmap中,尝试若干次后输出最短的路径长度以及对应的最短路径。
代码演示:
1.我们首先定义两个全局变量
public static Integer count=0;//每次成功到达终点所用的步数
public static Integer successCount=0;//记录成功次数
2.边界初始化函数
public static String[][] init(){
//1.初始化一个6x6地图
String[][] map=new String[6][6];
for(int row=0;row<6;row++){
for(int col=0;col<6;col++){
map[row][col]="0";
}
}
//2.初始化边界
//第一行和最后一行置1
for(int col=0;col<6;col++){
map[0][col]="1";
map[5][col]="1";
}
//第一列和最后一列置1
for(int row=0;row<6;row++){
map[row][0]="1";
map[row][5]="1";
}
//设置一些挡板
for(int col=0;col<2;col++){
map[2][col]="1";
map[3][5-col]="1";
}
//3.看一下初始边界
/*for(int row=0;row<6;row++){
for(int col=0;col<6;col++){
System.out.print(map[row][col]+" ");
}
System.out.println();
}*/
return map;
}
3.模式选择函数,选择向上还是左、右、下?
public static boolean mode(int index,String[][] map,int i,int j){
switch (index){
case 1:return go(map,i,j+1);
case 2:return go(map,i,j-1);
case 3:return go(map,i+1,j);
case 4:return go(map,i-1,j);
default:throw new RuntimeException("模式选择有误!");
}
}
4.取map中最小的key
//取出map中最小的key
public static Integer getMinKey(Map<Integer,String[][]> map){
if(map==null){
return null;
}else{
Set<Integer> set=map.keySet();
Object[] array = set.toArray();
Arrays.sort(array);
return (Integer)array[0];
}
}
5.游走函数(利用递归)
//对单个点的定义如下:
// 0:未走过
// 1:障碍物
// ^:已走
// x: 已走,但是死路
// 假设目标是右下角,即 map[4][4],起始坐标(i,j),遵循上、右、下、左的寻路原则
public static boolean go(String[][] map,int i,int j){
//随机生成1-4四个数字,选择不同的前进方向
Random random=new Random();
int[] mode=new int[4];
for(int num=0;num<4;num++){
mode[num] = random.nextInt(4)+1;
}
//如果地图右下角的点已经置“ ^ ”,就表明已经走通了,成功了耶!
if(map[4][4].equals("^")){
System.out.print("成功计数:"+(++successCount)+",");
if(count!=0) System.out.println("本次游走距离为"+count);
return true;
//这个点没走过的话就标记走过,然后继续寻路
}else {
if(map[i][j].equals("0")){
map[i][j]="^";
count+=1;
if(mode(mode[0],map,i,j)){
return true;
}else if(mode(mode[1],map,i,j)){
return true;
}else if(mode(mode[2],map,i,j)){
return true;
}else if(mode(mode[3],map,i,j)){
return true;
//如果上下左右都走不通,那就标记为“ X ” ,此路不通请绕路!
}else {
map[i][j]="x";
//有时走到死路的话需要返回重走,但走死路的这几步我不能算上去吧
count-=1;
return false;
}
//反正走不下去了,失败!
}else {
return false;
}
}
}
6.主函数
public static void main(String[] args) {
int tryTime=100;//总尝试次数
System.out.println("总尝试次数:"+tryTime);
//保存每次成功游走的结果
Map<Integer,String[][]> result=new HashMap<>();
while(tryTime>0){
String[][] map=init();
//如果成功走到终点了就记录下这次的结果
if(go(map, 1, 1)){
result.put(count,map);
count=0;
}
tryTime--;
}
System.out.println();
//取出最短路径长度及最短路径
System.out.print("最短路径为,"+getMinKey(result));
System.out.println("对应的路径如下:");
for(int row=0;row<6;row++){
for(int col=0;col<6;col++){
System.out.print(result.get(getMinKey(result))[row][col]+" ");
}
System.out.println();
}
}
7.测试:
总尝试次数:100
成功计数:1,本次游走距离为11
成功计数:2,本次游走距离为7
成功计数:3,本次游走距离为7
成功计数:4,本次游走距离为9
成功计数:5,本次游走距离为11
成功计数:6,本次游走距离为7
成功计数:7,本次游走距离为11
成功计数:8,本次游走距离为7
成功计数:9,本次游走距离为7
成功计数:10,本次游走距离为7
成功计数:11,本次游走距离为7
成功计数:12,本次游走距离为7
成功计数:13,本次游走距离为7
成功计数:14,本次游走距离为7
成功计数:15,本次游走距离为7
成功计数:16,本次游走距离为9
成功计数:17,本次游走距离为7
成功计数:18,本次游走距离为9
成功计数:19,本次游走距离为7
成功计数:20,本次游走距离为7
成功计数:21,本次游走距离为11
成功计数:22,本次游走距离为7
成功计数:23,本次游走距离为9
成功计数:24,本次游走距离为11
成功计数:25,本次游走距离为11
成功计数:26,本次游走距离为7
成功计数:27,本次游走距离为11
成功计数:28,本次游走距离为9
成功计数:29,本次游走距离为9
成功计数:30,本次游走距离为7
成功计数:31,本次游走距离为9
最短路径为,7对应的路径如下:
1 1 1 1 1 1
1 ^ ^ x x 1
1 1 ^ x x 1
1 x ^ ^ 1 1
1 x x ^ ^ 1
1 1 1 1 1 1