资讯专栏INFORMATION COLUMN

回溯算法

ctriptech / 680人阅读

摘要:回溯算法主要思想回溯算法的基本思想是从一条路往前走,能进则进,不能进则退回来,换一条路再试。回溯算法说白了就是穷举法。回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。用回溯算法解决问题的一般步骤为定义一个解空间,它包含问题的解。

回溯算法 主要思想

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。用回溯算法解决问题的一般步骤为:

定义一个解空间,它包含问题的解。

利用适于搜索的方法组织解空间。

利用深度优先法搜索解空间。

利用限界函数避免移动到不可能产生解的子空间。

解决迷宫问题 解决思想

将迷宫问题对应为二维数组,数组中只有两种值0和1,其中0,1分别表示通路和墙。不过在解决这个问题的时候一般要在最外面添加一个围墙,这里设置每个围墙都为1,这样有利于防止当走到了迷宫的出口处还会向前走,这个并不一定,只是最一般的方法,也是最有利于理解的方法。这里的利用到了回溯法,需要走到了一个位置,然后向四处试探,如果有一个方向可以走了就将当前的点压入栈,并且标记当前点以便于区分是否走过,如果四处都无出路,只需要回到前一个走到的点,然后从前一个点再换一个方向重新走

代码
import java.util.Stack;

/**
 * Created by chenjiabing on 17-5-5.
 */
class position {
    public int row;
    public int col;

    public position(int row, int col) {
        this.col = col;
        this.row = row;
    }

    public position() {
        row = 0;
        col = 0;
    }

    public String toString() {
        return "(" + (row - 1) + " ," + (col - 1) + ")";
    }  //这里由于四周围上了墙,所以这里的输出就要在原来的基础上减一
}


class Main {
    private int[][] maze = null;
    private Stack stack = null;  //创建一个栈用于存储状态
    private int row;   //行数
    private int col;
    boolean[][] p = null;    //这里的p是用来标记已经走过的点,初始化为false

    public boolean end(int i, int j) {
        return i == row && j == col;
    }

    public Main(int[][] maze) {
        stack = new Stack();
        row = maze[0].length;// 行数
        col = maze.length;   //列数
        p = new boolean[row + 2][col + 2];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                p[i][j] = false;    //初始化
            }
        }
        this.maze = maze;


    }

    public void findPath() {

        //创建一个新的迷宫,将两边都围上墙,也就是在四周都填上1的墙,形成新的迷宫,主要的目的就是防止走到迷宫的边界的出口的位置还会继续向前走
        //因此需要正确的判断是否在边界线上,所以要在外围加上一堵墙,
        int[][] temp = new int[row + 2][col + 2];
        for (int i = 0; i < row + 2; i++) {
            for (int j = 0; j < col + 2; j++) {
                temp[0][j] = 1;   //第一行围上
                temp[row + 1][j] = 1;  //最后一行围上
                temp[i][0] = temp[i][col + 1] = 1;  //两边的围上
            }
        }


        // 将原始迷宫复制到新的迷宫中
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                temp[i + 1][j + 1] = maze[i][j];
            }
        }


        int i = 1;
        int j = 1;
        p[i][j] = true;
        stack.push(new position(i, j));
        //这里是是将走到的点入栈,然后如果前后左右都走不通的话才出栈
        while (!stack.empty() && !end(i, j)) {


           //下面就开始在四周试探,如果有路就向前走,顺序分别是右,下,上,左,当然这是随便定义的,不过一般都是现向下和右的
            if (temp[i][j + 1] == 0 && p[i][j + 1] == false)//这里如果不在四周加上墙,那么在到达边界判断的时候就会出现超出数组的索引的错误,因为到达边界再加一就会溢出
            {
                p[i][j + 1] = true;
                stack.push(new position(i, j + 1));
                j++;
            } else if (temp[i + 1][j] == 0 && p[i + 1][j] == false)//如果下面可以走的话,讲当前点压入栈,i++走到下一个点
            {
                p[i + 1][j] = true;
                stack.push(new position(i + 1, j));
                i++;
            } else if (temp[i][j - 1] == 0 && p[i][j - 1] == false) {
                p[i][j - 1] = true;
                stack.push(new position(i, j - 1));
                j--;
            } else if (temp[i - 1][j] == 0 && p[i - 1][j] == false) {
                p[i - 1][j] = true;
                stack.push(new position(i - 1, j));
                i--;
            } else   //前后左右都不能走
            {
                System.out.println(i + "---------" + j);
                stack.pop();   //这个点不能走通,弹出
                if (stack.empty())      //如果此栈中已经没有点了,那么直接跳出循环
                {
                    System.out.println("没有路径了,出不去了");
                    return;    //直接退出了,下面就不用找了
                }
                i = stack.peek().row;   //获得最新点的坐标
                j = stack.peek().col;

            }

            //如果已经到达了边界,那么直接可以出去了,不需要继续向前走了,这里是规定边界的任意为0的位置都是出口
            //如果不加这个判断的话,那么当到达边界的时候,只有走到不能再走的时候才会输出路线,那种线路相对这个而言是比较长的
            if (j == temp[0].length - 2) {   //如果已经到达边界了,那么当前的位置就是出口,就不需要再走了
                Stack pos = new Stack();

                System.out.println("路径如下:");

                for (int count = 0; count < stack.size(); count++) {
                    System.out.println(stack.elementAt(count));
                }


            }
        }


    }

    public static void main(String args[]) {
        int[][] maze = {
                {0, 1, 0, 0, 0},
                {0, 1, 0, 1, 0},
                {0, 0, 0, 0, 0},
                {0, 1, 1, 1, 0},
                {0, 0, 0, 1, 0}
        };
        Main main = new Main(maze);
        main.findPath();

    }

}
更多文章请移步本人博客

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70021.html

相关文章

  • 回溯算法讲解--适用于leetcode绝大多数回溯题目

    摘要:什么是回溯算法回溯法是一种系统搜索问题解空间的方法。解空间定义为与数字长度相同。最后,为什么要掌握回溯法因为懂了回溯法之后笔试里的很多题就算不了,起码成功运行到之间是没问题的。 什么是回溯算法?回溯法是一种系统搜索问题解空间的方法。为了实现回溯,需要给问题定义一个解空间。说到底它是一种搜索算法。只是这里的搜索是在一个叫做解空间的地方搜索。而往往所谓的dfs,bfs都是在图或者树这种数据...

    saucxs 评论0 收藏0
  • LeetCode 关于回溯问题的看法

    摘要:回溯算法在算法过程中就是类似于枚举算法,尝试在搜索过程中找到问题的解。 回溯算法( BackTrack )在算法过程中就是类似于枚举算法,尝试在搜索过程中找到问题的解。 使用回溯算法解题的一般步骤 使用回溯算法解题的一般步骤: 针对所给问题得出一般的解空间 用回溯搜索方法搜索解空间 使用深度优先去搜索所有解并包含适当的剪枝操作 LeetCode 使用回溯算法的题目主要有 36 题,...

    ASCH 评论0 收藏0
  • LeetCode偶尔一题 —— 39. Combination Sum(回溯算法系列)

    摘要:输入输出分析题目由于我们需要找到多个组合,简单的使用循环肯定是不行的,这时候我们可以使用回溯算法来解决这个问题。用回溯算法解决问题的一般步骤针对所给问题,定义问题的解空间,它至少包含问题的一个最优解。 题目描述 Given a set of candidate numbers (candidates) (without duplicates) and a target number ...

    linkin 评论0 收藏0
  • 基本算法思想:递归+分治+动态规划+贪心+回溯+分支限界

    摘要:代码实现见下面评论对应代码动态规划基本思想和分治法基本思想有共同的地方,不同的是子问题往往不是独立的,有事母问题要借助子问题的解来判断,因此把已经计算好的问题记录在表格中,后续如果需要查询一下,可以避免重复计算,这是动态规划的基本思想。 作者:心叶时间:2018-05-01 19:28 本文对应github地址:https://github.com/yelloxing/... 以上实现...

    EscapedDog 评论0 收藏0

发表评论

0条评论

ctriptech

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<