目录

前言

一、迷宫寻路问题

二、回溯算法

三、迷宫问题的解决

总结

利用回溯算法解决实际经典问题


前言

前一篇文章一起来谈了用Java实现的递归问题,具体见下链接:

这个时候可能有读者就会觉得,用循环不就好了,为什么要用递归?递归的话还很占用内存呢,使用不当还会造成栈溢出,确实,在解决一些简单问题时,循环确实比递归要优一点,但是,在解决一些复杂问题的时候,还会这样觉得吗?


一、迷宫寻路问题

有一个8X8的迷宫,周围是一堵墙,有一个人站在(1,1)处,现在要求利用计算机画出这个人可以行走的路线:

Java走迷宫键盘方向键控制代码_java

 以上的迷宫问题,我们可以转化为数字进行表示。比如上方的蓝色部分,我们可以用数字1来代替,绿色部分用数字0来代替。

Java走迷宫键盘方向键控制代码_Java走迷宫键盘方向键控制代码_02

在这个二维数组中,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语句,继续判断,此时,函数已经实现了回溯功能,条件不符合之后进行下一个判断,直到把所有的可能试出来,用一个图表示可以是这样:

Java走迷宫键盘方向键控制代码_Java走迷宫键盘方向键控制代码_03

此时注意字母的顺序,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,标记为死路,然后计算机继续寻路,直到找到最终的结果,程序运行结束。

Java走迷宫键盘方向键控制代码_开发语言_04

总结

最后发表一下自己对回溯算法的看法:真的太智能了,真不知道哪个大佬想出来的,头发掉精光吧~,但是回溯算法理解起来也是有比较大的难度,要多理解,多思考才行。那同时回溯算法也能解决很多如可能性问题、最优解问题、总数问题等。

利用回溯算法解决实际经典问题