目录
前言
一、迷宫寻路问题
二、回溯算法
三、迷宫问题的解决
总结
利用回溯算法解决实际经典问题
前言
前一篇文章一起来谈了用Java实现的递归问题,具体见下链接:
这个时候可能有读者就会觉得,用循环不就好了,为什么要用递归?递归的话还很占用内存呢,使用不当还会造成栈溢出,确实,在解决一些简单问题时,循环确实比递归要优一点,但是,在解决一些复杂问题的时候,还会这样觉得吗?
一、迷宫寻路问题
有一个8X8的迷宫,周围是一堵墙,有一个人站在(1,1)处,现在要求利用计算机画出这个人可以行走的路线:
以上的迷宫问题,我们可以转化为数字进行表示。比如上方的蓝色部分,我们可以用数字1来代替,绿色部分用数字0来代替。
在这个二维数组中,1就代表墙,0代表可走的路径。在这里我们可以令起点为(1,1),终点为(6,6),此时来解决这个迷宫的路径问题。
这个时候问题就来了,我们怎么让计算机实现一个自动寻路功能呢?那这里就得用到回溯算法了。
二、回溯算法
什么是回溯算法呢?回溯算法实际上就是一个搜索和尝试的过程,主要是在搜索尝试过程中寻找解,当发现已不满足求解条件时,就返回到上一个状态,称为回溯,尝试别的路径。要实现回溯的功能,那就一定离不开递归了。递归如何实现回溯呢?我们用一个简单的if语句来看一看:
public static void main(String[] args) {
Example(3);
}
public static int Example(int i) {
if (i==1) {
return i;
}
if (Example(i-1) > 3) {
return i;
} else if (Example(i-1) > 2) {
return i;
}
return i;
}
我们可以看到这里,假设我们从3开始调用,这个时候会进if语句,很显然第一个语句不符合条件,继续往下走,我们看到第二个语句用了递归。递归之后,i的值为2,同样进行递归,直到i等于1的时候,返回了个1,此时回到i等于2的那个语句,一判断,不符合条件。此时,函数走到了else if语句,继续判断。
到这里我们就不继续分析代码了,因为这串代码本来也没什么实际意义。我们看到,当if不符合之后,走到了else if语句,继续判断,此时,函数已经实现了回溯功能,条件不符合之后进行下一个判断,直到把所有的可能试出来,用一个图表示可以是这样:
此时注意字母的顺序,A首先调用了B,B又调用C,此时若C满足一定条件,返回B重新调用D,D调用完成之后也进行返回,此时B调用完毕,回到A,A此时开始调用E,同理,调用了F和G。在这个过程中,计算机按照这个顺序依次执行了所有的结果,当末尾的结果执行完毕之后,进行回溯,继续执行下一个功能。
三、迷宫问题的解决
理解了回溯算法之后,我们利用回溯算法解决迷宫问题,看如下的代码:
public static void main(String[] args) {
int[][] map = new int[8][8];
for (int i = 0; i < map.length; i++) {
map[i][0] = 1;
map[i][map.length-1] = 1;
}
for (int i = 0; i < map[0].length; i++) {
map[0][i] = 1;
map[map.length-1][i] = 1;
}
map[4][1] = 1;
map[4][2] = 1;
map[4][3] = 1;
map[2][4] = 1;
map[2][5] = 1;
map[2][6] = 1;
setWars(map , 1,1,6,6);
printArray(map);
}
public static boolean setWars(int[][] map , int i , int j , int endI , int endJ) {
if (map[endI][endJ] == 2) {
return true;
}
else {
if (map[i][j] == 0) {
map[i][j] = 2;
if (setWars(map , i , j+1 , endI , endJ)) {
return true;
}
else if (setWars(map,i+1 , j , endI , endJ)) {
return true;
} else if (setWars(map,i-1 , j , endI , endJ)) {
return true;
}
else if (setWars(map,i , j-1 , endI , endJ)) {
return true;
} else {
map[i][j] = 3;
return false;
}
}
else {
return false;
}
}
}
public static void printArray(int[][] map) {
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进行了表示,随后我们调用了setWars函数。
在函数中,我们使用了一个if,三个else if,如此设计的目的是,一个else if代表一个方向(上下左右四个方向),如果一个方向可以,则继续往下走,直到发现上下左右都不行了之后,便开始回溯,将走过的路设置为3,标记为死路,然后计算机继续寻路,直到找到最终的结果,程序运行结束。
总结
最后发表一下自己对回溯算法的看法:真的太智能了,真不知道哪个大佬想出来的,头发掉精光吧~,但是回溯算法理解起来也是有比较大的难度,要多理解,多思考才行。那同时回溯算法也能解决很多如可能性问题、最优解问题、总数问题等。